ArmMiteF4 BITBANG BITSTREAM Operation


Author Message
morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 11:54am 21 Mar 2024      

Using the ArmmiteF4 with MM V5.07.02b2.
@phil99 coded  NECSend for the Picomite using MM.
Const IRpin = MM.Info(pinno gp1)  'output pin, change as needed
SetPin IRpin, dout : Pin(IRpin) = 0 ' IR code output

Do
'Your program here.
Input "Enter Device and Key codes "; dev, key
NECsend IRpin, dev, key
Loop

Sub NECsend IRpin As integer, dev As integer, key As integer
Local integer word, d(2200), m, n = 1

word = (dev<<16) + (key<<8) + ((255 Xor key )And 255) 'assemble 32 bits

Math set 13 , d() '13=1000/(38*2)uS duration of a half cycle @ 38kHz
d(672) = 4500  'pause after start pulse train

For m = 714 To 2100 Step 42  'load data after start sequence
  d(m) = (((word>>(32-n)) And 1)*2 + 1) * 562.5  'convert bits to duration (short = 0, long = 1)
  Inc n 'step to next data bit
Next

Device bitstream IRpin, 2199, d() 'send the data

Pause 100
Pin(IRpin) = 0
End Sub


This works for an Onkyo receiver. (Also with 'Device'command replaced with Bitbang)

The above code with the IRpin changed to (pinno PE0} on the ArmmiteF4 does not work.

After investigation it appears that "d(672) = 4500  'pause after start pulse train" is not interpreted by BITSTREAM.
It shows up in the d(0) to d(800) bitstream exactly the same as the picomite but does not result in the 4.5mS space after the leading pulse burst.

test values are devicecode  19832 keycode 192.

Picomite





ArmmiteF4





Tony

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 12:42pm 21 Mar 2024      

That is odd. I will test on an F4 tomorrow, getting a bit late now.
Your scope pics indicate all the bitstream array index values are off by 1 anyway. It doesn't affect anything I have but I will tidy it up anyway.

Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3691
Posted: 01:39pm 21 Mar 2024      

Phil,

There is more that is weird.
The bursts are 42 cycles of 13us = 546us (not exacly 562, but close)
The pauses between the bursts must be 562us (logic 0) -or- 1686us (logic 1).

Look at both scope plots. Either the burst timing is wrong, or the pause timing is wrong. Visually there is simply too much burst visible.
Checkif a single cycle is 13+13=26us. It feels the burst is 800us, in which case 13us is more 20us, 38kHz is 25kHz.

Volhout

P.S.... this feels like a job for PIO. Shift the 32 bit NEC value in FIFO, and let PIO do it's job. But I guess this is for the F4. No PIO.

Volhout
Edited 2024-03-21 23:47 by Volhout

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 04:10am 22 Mar 2024      

It seems the F4 Bitbang Bitstream won't accept times greater than 3000uS so the 4500uS value gets corrupted.
  Manual said  The array contains the length of each level in the bitstream in
microseconds. The maximum period allowed is 65.5 mSec
So something seems to be wrong.
Also the modulation frequency is a little higher (39.8kHz) than for the Pico so the duration of the half-cycles has been increased from 13 to 14uS.
Now it is a little low but close enough.

The apparently wrong timing pointed out by Volhout are correct on my scope and the Sub works on my devices.
Perhaps a display artefact in the images?

'Work-around for problems with Bitbang Bitstream on F4
Const IRpin = MM.Info(pinno PA0)  'output pin, change as needed
SetPin IRpin, dout : Pin(IRpin) = 0 ' IR code output

Do
'Your program here.
 NECsend IRpin, dev, key
Loop

Sub NECsend IRpin As integer, dev As integer, key As integer
'Version for ARMmite F407 MMBasic Version 5.07.02b2
Local integer word, d(1389), m, n = 1, c(671)

word = (dev<<16) + (key<<8) + 255-key 'assemble 32 bits

Math set 14 , c() '13=1000/(38*2)uS duration of a half cycle @ 38kHz 9mS start burst
Math set 14 , d() '13=1000/(38*2)uS duration of a half cycle @ 38kHz main bitstream

BitBang bitstream IRpin, 670, c() 'send 9ms start 38kHz
Pause 1.5 '4.5mS pause after start pulse train - allowing for 3mS processing delays @ 168MHz CPU

For m = 43 To 1387 Step 42  'load data after start sequence
 d(m) = (((word>>(32-n)) AND 1)*2 + 1) * 562.5  'convert bits to duration (short = 0, long = 1)
 Inc n 'step to next data bit
Next

BitBang bitstream IRpin, 1387, d() 'send the data

Pin(IRpin) = 0
End Sub


Footnote added 2024-03-22 15:02 by phil99
Missed a line in the Do Loop.
Input "Enter Device and Key codes "; dev, key

As per the first version.

morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 04:39am 22 Mar 2024      

Hi Phil99,

Thanks for this. I will give it a run shortly. Looking at the display resolution, I think this is the cause of the broad pulses. Having tried various values for 4.5ms pause I believed the problem was in the MMBasic coding for the F4. This is a great sub that is easy to use compared to getting a CSub to work.

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 04:55am 22 Mar 2024      

Updated PicoMite version
Sub NECsend IRout As integer, dev As integer, key As integer
'Picomite V5.08.00 version
 Local integer word, d(2066), m, n = 1

 word = (dev<<16) + (key<<8) + 255-key '32bit word = Device 16b + Key 8b + inverted Key 8b

 Math set 13, d() 'fill array with half-cycles, 13uS = duration of a half cycle @ 38kHz
 d(691) = 4500  'uS pause after 9mS of 38kHz to start

 For m = 733 To 2065 Step 42  'load data after start sequence
  d(m) = (((word>>(32-n)) And 1)*2 + 1) * 562.5'convert bits to pauses (0=562uS, 1=1687uS)
  Inc n 'step to next data bit
 Next

 Device bitstream IRout, 2066, d() 'send the data

 Pin(IRout) = 0
End Sub

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 05:23am 22 Mar 2024      

A more accurate way to get the 4.5mS pause. The other was dependant on the CPU speed of the F4.
Do
'Your program here.
 Input "Enter Device and Key codes "; dev, key
 NECsend IRpin, dev, key
Loop

Sub NECsend IRpin As integer, dev As integer, key As integer
'Version for ARMmite F407 MMBasic Version 5.07.02b2
Local integer word, d(1389), m, n = 1, c(671)

word = (dev<<16) + (key<<8) + 255-key 'assemble 32 bits

Math set 14 , c() '13=1000/(38*2)uS duration of a half cycle @ 38kHz
Math set 14 , d() '13=1000/(38*2)uS duration of a half cycle @ 38kHz

BitBang bitstream IRpin, 670, c() 'send 9ms start 38kHz
Timer =0
For m = 43 To 1387 Step 42  'load data after start sequence
 d(m) = (((word>>(32-n)) AND 1)*2 + 1) * 562.5  'convert bits to duration (short = 0, long = 1)
 Inc n 'step to next data bit
Next
Do : Loop Until Timer > 4.3
BitBang bitstream IRpin, 1387, d() 'send the data

Pin(IRpin) = 0
End Sub

Edited 2024-03-22 15:24 by phil99

morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 06:24am 22 Mar 2024      

Hi phil99

  Quote  A more accurate way to get the 4.5mS pause.


Perfect!
Thanks, Tony

disco4now

Guru

Joined: 18/12/2014
Location: Australia
Posts: 854
Posted: 06:49am 22 Mar 2024      

There is a couple of problems with F4 BITBANG. Its currently 21/20 too fast and wraps around when you ask for greater than 65565/20 uS. I will try to fix and post an update.
Regards
Gerry

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 07:00am 22 Mar 2024      

Thankyou for such prompt attention. Not serious issues as there are simple ways to cope with both.

Yet another revision. Array c() is unnecessary.
Do
'Your program here.
 Input "Enter Device and Key codes "; dev, key
 NECsend IRpin, dev, key
Loop

Sub NECsend IRpin As integer, dev As integer, key As integer
Local integer word, d(1389), m, n = 1

word = (dev<<16) + (key<<8) + 255-key 'assemble 32 bits

Math set 14 , d() '13=1000/(38*2)uS duration of a half cycle @ 38kHz

BitBang bitstream IRpin, 670, d() 'send 9ms start 38kHz
Timer =0
For m = 43 To 1387 Step 42  'load data after start sequence
 d(m) = (((word>>(32-n)) AND 1)*2 + 1) * 562.5  'convert bits to duration (short = 0, long = 1)
 Inc n 'step to next data bit
Next
Do : Loop Until Timer > 4.3
BitBang bitstream IRpin, 1387, d() 'send the data

Pin(IRpin) = 0
End Sub

Edited 2024-03-22 17:09 by phil99

Footnote added 2024-03-24 07:25 by phil99
In the event that the program using the NECsend Sub is also using the Timer odd things may hapen when the Sub resets it. These mods fix that.
T = Timer + 4.3
For
...
Next
Do : Loop Until T < Timer


Footnote added 2024-03-24 07:30 by phil99
Also add this line.
Local Float T

Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3691
Posted: 10:09am 22 Mar 2024      

Yes !
Smart use of d(). Very good. Also memory efficient.
Not waste time, build the d() array while waiting for 4.5ms (4.3) to pass. Smart.

Volhout
Edited 2024-03-22 20:12 by Volhout

morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 11:17am 22 Mar 2024      

@phil99 thanks for getting this going.

@disco4now will monitor for your input. Thanks for your work on the F4. I have several sitting around as I stopped using them when it became a problem it the battery failed. Now am dusting them off and having fun.

Regards
Tony

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 12:23pm 22 Mar 2024      

It is a bit fiddly to do but you can replace the cell holder with a CR2032 one.
Lasts much longer.

morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 06:59am 23 Mar 2024      

Here is code that incorporates bit reversing as NEC protocol is LSB bit first.
This means that the ARMmiteF4 device and key codes match that of the Micromite.
'22/3/2024 - new NECSend routine to replace original due problem with 4.5mS space generation
 ' modified to use same dev code and key code as micromite
 
 ' IR Transmit
 Const IRpin = MM.Info$(pinno PE0)  'output pin, change as needed
 SetPin IRpin, dout : Pin(IRpin) = 0 ' IR code output
 ' IR receiver input, fixed as PE2 for F4
 '
 
 
 Do
   'Your program here.
   Input "Enter Device and Command codes "; devin, keyin    
   ConvInput ' use function revb to reverse  bits in a word as NEC IR code sent LSB first
   NECsend IRpin, DevCode, KeyCode
   
   pause 1000
 Loop
 
 
Sub NECsend IRpin As integer, dev As integer, key As integer
 Local integer word, d(1389), m, n = 1

 word = (DevCode<<16) + (KeyCode<<8) + 255-KeyCode 'assemble 32 bits
 
 Math set 14 , d() '13=1000/(38*2)uS duration of a half cycle @ 38kHz
 BitBang bitstream IRpin, 670, d() 'send 9ms start 38kHz
 Timer =0
 For m = 43 To 1387 Step 42  'load data after start sequence
   d(m) = (((word>>(32-n)) AND 1)*2 + 1) * 562.5  'convert bits to duration (short = 0, long = 1)
   Inc n 'step to next data bit
 Next
 Do : Loop Until Timer > 4.3
 BitBang bitstream IRpin, 1387, d() 'send the data
 
 Pin(IRpin) = 0
End Sub
 '-----------------------------
sub ConvInput  ' use function revb to reverse  bits in a word as NEC IR code sent LSB first
 'Check if device code is 8 bit or 16 bit
 If devin  < 256 Then ' 8 bit address, so add inverse of address
   DevCode=(revb(devin,8)<<8) + 255-(revb(devin,8))'add inverse to form 16 bits
     Else ' extended or 16 bit address
   DevCode=revb(devin,16)
 end if
 KeyCode=revb(keyin,8)
 print
 print "Input- device ";devin;"   "; bin$(devin);"   keyin ";keyin;"   ";Bin$(keyin)
 print "Output- DevCode "; DevCode;"  ";BIN$(DevCode,16);"  ";"   KeyCode ";KeyCode;"   ";Bin$(KeyCode,8)
 Print
 
end sub
 
 ' Function to reverse  bits in byte as NEC IR code sent LSB first
Function revb(x As integer, y As integer) As integer
 Local integer z
 revb=0
 For z=1 To y
   'select bit starting from RHS to LHS
   If x And (1<<(z-1)) Then
     revb=revb Or (1<<(y-z))
   EndIf
 Next z
End Function


Also to read IR codes use:
'F4-NECRx-bit-reverse
 ' modified to use same dev code and key code as the micromite
 
 
 ' IR receiver input, fixed as PE2 for F4
 '
 IR dev, key , irint 'Rx.int 'start the decoder
 Print "Waiting for IR input"
 
 
 Do
   'Your program here.
   
   pause 1000
 Loop
 
 
 'process detected IR input
irint:
 If (dev And &HFF) = ((dev>>8) Xor &HFF) Then ' normal address
   Print "device address 8 bit "
   DevCode=revb((dev>>8),8)
   KeyCode=revb(key,8)
 Else ' extended address
   Print "device address 16 bit "
   DevCode=revb(dev,16)
   KeyCode=revb(key,8)
 EndIf
 'print "device ";dev;"   "; bin$(dev,16);"   command ";key;"   ";Bin$(key,8)
 print "DevCode ";DevCode,"   "; bin$(DevCode,8);"  KeyCode ";KeyCode;"   ";Bin$(KeyCode,8)
 print
 IReturn
 
 ' Function to reverse  bits in byte as NEC IR code sent LSB first
Function revb(x As integer, y As integer) As integer
 Local integer z
 revb=0
 For z=1 To y
   'select bit starting from RHS to LHS
   If x And (1<<(z-1)) Then
     revb=revb Or (1<<(y-z))
   EndIf
 Next z
End Function
 


Tony

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 08:01am 23 Mar 2024      

I guess the bit reversal could be incorporated in the NECsend sub but the Dev and Key codes I used for testing are from the built-in IR Receive function. They work on all my devices without reversing.
Perhaps the output of that is already reversed.

When Gerry / @disco4now gets Bitstream fixed just change:-

Math set 14, d()
back to
Math set 13, d()

The split Bitstream should still work without change.

morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 09:34am 23 Mar 2024      

It's a compatibility thing. The reason I did it was so that it gives the same device and key codes for the NECSend (and NEC receive) as the MX170 Micromite code gives.

phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 1852
Posted: 11:44am 24 Mar 2024      

Some other bit reversal options in this thread.
This one is very clever.
>  b%=73 : ? bin$(b%,8)
01001001
> Rb% = (b% * &H0202020202 and &H010884422010) mod 1023
> ? bin$(Rb%,8)
10010010
>


Footnote added 2024-03-25 07:16 by phil99
Using the above on a 16 bit device code.
> b% = 54321 : ? bin$(b%,16)
1101010000110001
> Rb% = (((b% and 255) * &H0202020202 and &H010884422010) mod 1023) << 8
> Rb% = Rb% + ((b% >> 8) * &H0202020202 and &H010884422010) mod 1023
> ? bin$(Rb%,16)
1000110000101011
>


Footnote added 2024-03-25 07:22 by phil99
Or this.
> b% = 54321 : ? bin$(b%,16)
1101010000110001
> Rb% = (((b% and 255) * &H0202020202 and &H010884422010) mod 1023) << 8
> inc Rb%, ((b% >> 8) * &H0202020202 and &H010884422010) mod 1023
> ? bin$(Rb%,16)
1000110000101011
>

morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 12:12pm 24 Mar 2024      

Very interesting. Time to look at doing it better. TBS is a great resource. It's a long time since I have done any programming.

morgs67
Regular Member

Joined: 10/07/2019
Location: Australia
Posts: 73
Posted: 11:14am 25 Mar 2024      

Tuned up with reversal code mentioned above.
Device and key code match original micromite input and output codes.

Transmit-
'Using 'Bit Twiddling Hacks' (Search on the Internet) pointed to by phil99 ,matherp
 '22/3/2024 - new NECSend routine to replace original due problem with 4.5mS space generation
 'uses the same dev code and key code as the micromite
 
 ' IR Transmit
 Const IRpin = MM.Info$(pinno PE0)  'output pin, change as needed
 SetPin IRpin, dout : Pin(IRpin) = 0 ' IR code output
 ' IR receiver input, fixed as PE2 for F4
 '
 
 Do
   'Your program here.
   Input "Enter Device and Command codes "; dev, key
   NECsend IRpin, dev, key
   
   pause 1000
 Loop
 
 
Sub NECsend IRpin As integer, dev As integer, key As integer
 Local integer word, d(1389), m, n = 1
 'need to reverse  bits in a byte as NEC IR code is sent LSB first
 If dev  < 256 Then ' 8 bit address, so add inverse of address
   DevCode = ((dev * &H0202020202 and &H010884422010) mod 1023)<<8
   DevCode = DevCode + (255-((dev * &H0202020202 and &H010884422010) mod 1023))
 Else ' extended or 16 bit address
   DevCode = (((dev and 255) * &H0202020202 and &H010884422010) mod 1023) << 8
   DevCode = DevCode + ((dev>> 8) * &H0202020202 and &H010884422010) mod 1023
 end if
 KeyCode = (key * &H0202020202 and &H010884422010) mod 1023
 'Check operation of bit reverse code
 print
 print "Input- device ";dev;"   "; bin$(dev);"   key ";key;"   ";Bin$(key)
 print "Output- DevCode "; DevCode;"  ";BIN$(DevCode,16);"  ";"   KeyCode ";KeyCode;"   ";Bin$(KeyCode,8)
 Print
 '
 word = (DevCode<<16) + (KeyCode<<8) + 255-KeyCode 'assemble 32 bits to transmit
 '
 Math set 14 , d() '14=1000/(38*2)uS duration of a half cycle @ 38kHz
 BitBang bitstream IRpin, 670, d() 'send 9ms start 38kHz
 Timer =0
 For m = 43 To 1387 Step 42  'load data sent after start sequence
   d(m) = (((word>>(32-n)) AND 1)*2 + 1) * 562.5  'convert bits to duration (short = 0, long = 1)
   Inc n 'step to next data bit
 Next
 Do : Loop Until Timer > 4.3 'send 4.5ms start space
 BitBang bitstream IRpin, 1387, d() 'send the data
 Pin(IRpin) = 0
End Sub
 


IR received decoder-

'Using 'Bit Twiddling Hacks' (Search on the Internet) pointed to by phil99 ,matherp
 'uses the same device code and key code as the micromite
 
 'IR receiver input, fixed as PE2 for F4
 
 foreCol% = RGB(white)
 backCol% = rgb(BLACK)
 CLs
 
 IR dev, key , irint 'start the decoder
 Print "   Waiting for IR input"
 text 1,20,"   Waiting for IR input",LT,1,1,foreCol%,backCol%
 Do
   'Your program here.
   
   pause 1000
 Loop
 
 'IR input detected
irint:
 If (dev And &HFF) = ((dev>>8) Xor &HFF) Then ' 8 bit address
   Print "device address 8 bit "
   DevCode = ((dev>>8) * &H0202020202 and &H010884422010) mod 1023
 Else ' extended or 16 bit address
   Print "device address 16 bit "
   DevCode = (((dev and 255) * &H0202020202 and &H010884422010) mod 1023) << 8
   DevCode = DevCode + ((dev>> 8) * &H0202020202 and &H010884422010) mod 1023
 EndIf
 KeyCode = (key * &H0202020202 and &H010884422010) mod 1023
 '
 print "DevCode ";DevCode,"   "; bin$(DevCode,8);"  KeyCode ";KeyCode;"   ";Bin$(KeyCode,8)
 print
 cls
 text 1,20,"Device Code:"+STR$(DevCode)+"  Key Code:"+STR$(KeyCode),LT,1,1,foreCol%,backCol%
 '
 IReturn


Tony

Footnote added 2024-06-06 17:00 by morgs67
Update for transmit code:

Footnote added 2024-03-24 07:25 by phil99
In the event that the program using the NECsend Sub is also using the Timer odd things may hapen when the Sub resets it. These mods fix that.
T = Timer + 4.3 'Replaces Timer = 0
For
...
Next
Do : Loop Until T < Timer 'Replaces Do : Loop Until Timer > 4.3

Also add this line.
Local Float T