PDA

View Full Version : RF12 module need to convert some C to Basic



davewanna
- 9th June 2008, 06:45
Hi,

I have a few RFM12 modules that I have posted on here about before. I have started to delve into the coding side of things, but am somewhat lost. The datasheets for these modules have examples coded in C. But I currently have absolutely no knowledge of C. Can someone who knows picbasic, and C please point me in the right direction?

The module is 'programmable' for a LOT of options. I am interested in getting just one to work, then I should be able to find my way from there. It has an osc output that can be used to supply the pic. I currently have that working, but at the default 1Mhz. This is the setting I have been attempting to change, as it should be easy to spot.

The register is in the datasheet at: http://www.hoperf.com/pdf/RF12.pdf Page 22, item 14.

according to what I can see, changing that register to 1100000011110111 should change the osc output to 10Mhz, and the low voltage detector to 4.5V

The C example of how to do this, from what I can see uses a command called write.

Code example is at: http://www.hoperf.com/pdf/RF12_code.pdf Page 20.
The piece I'm particularly interested in is when it calls a subroutine called "WriteCMD()" and has the following:



void WriteCMD( uint CMD )
{
uchar n=16;
SCK=0;
nSEL=0;
while(n--)
{
if(CMD&0x8000)
Write1();
else
Write0();
CMD=CMD<<1;
}
SCK=0;
nSEL=1;
}



Can this be done in picbasic via shiftout, or serout?


I am using a 16f877a


Thanks in advance as always....

davewanna
- 9th June 2008, 08:13
As I said I don't know any C.. But looking at the code more, I have just noticed that Write0 and Write1 seem to also be subroutines...



void Write0( void )
{
SDI=0;
SCK=0;
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
SCK=1;
NOP();
}



My revised question is:
What is NOP() ????

mackrackit
- 9th June 2008, 08:25
Table 15-2 of the 16F877A data sheet says a NOP = No Operation

I can not help with the "C".

davewanna
- 9th June 2008, 08:28
Ok, so it seems that a NOP() is a nothing... which I assume is a timing thing. I can't really see how it turns a piece of HEX data into a string of ones and zero's and outputs them..

Surely there is a simpler way of doing this in picbasic? Should it respond to a serout command?

I thought I had researched all I could before I posted.... I know no-one is replying yet.. but I think asking the questions is helping my brain somehow.

Still open for assistance!

davewanna
- 9th June 2008, 08:30
Thanks mackrackit... I just found that out as you posted...

Pity no-one has example code of these modules in picbasic.. would make my learning procedure easier...

Ioannis
- 9th June 2008, 08:54
Have you just tried to send this binary string with shiftout?

I think there is not much needed to make it work. Just send the string. Serout will not work here.

Ioannis

davewanna
- 9th June 2008, 09:18
Ioannis,


I have not used shiftout before... I have just tried..




SCK var portb.6
SDO var portb.7
shiftout SDO, SCK, 1, [%1100000011110111]


This did not change the osc... still 1Mhz. Is my syntax ok?

Ioannis
- 9th June 2008, 12:18
If you check it with the manual, I understand that you cannot send a binary string that way. You have to use, as with most of the commands, a variable. Either byte or word size defined on top.

So put the string in two byte variables or a word sized variable and then send it out with shiftout command. Check also if MSB or LSB is needed by the module.

The 1100000011110111 string is exaclty 16bits.

Ioannis

dhouston
- 9th June 2008, 12:43
There is an example on the meLabs website showing how to use the synchronous serial port hardware to read/write from/to an SPI slave (e.g. RF12).http://www.melabs.com/resources/samples/pbp/spimast.basAlso, here is a link to a BasCOM program written for the RF12. While BasCOM is not PBP and an AVR chip is not a PIC, it should still give you a good understanding of what you need to do. http://comwebnet.co.funpic.de/RFM12/rf12Tranceiver.bas

davewanna
- 9th June 2008, 13:46
Ioannis,

I realised this not long after I posted.. I tried the following with no difference in results..



dout var word
dout = $C0F7
shiftout SDO, SCK, 1, [dout]



Tried with the HEX, and the binary values.
I don't think it matters, but also tried [dout\16]

Dhouston,

Thanks for the links, I will study them closely tomorrow.

skimask
- 9th June 2008, 14:13
void WriteCMD( uint CMD ) <-uint CMD takes in an unsigned integer
{
uchar n=16; <-unsigned character (byte) set to 16
SCK=0; <- SCK to logic low
nSEL=0; <- nSEL to logic low
while(n--) <- while/wend loop, n gets decremented by 1 each time thru
{
if(CMD&0x8000) <- if high bit of CMD is set then
Write1(); <-call the Write1() sub
else
Write0(); <-otherwise do this one
CMD=CMD<<1; <-shift it left by one
}
SCK=0; <- SCK to logic low
nSEL=1; <- nSEL to logic high
}


I might be totally wrong...not that familiar with C...but to me this means:



WriteCMD:
n = 16 : SCK = 0 : nSEL = 0
for nn = 16 to 0 step -1
if cmd.15 = 1 then
gosub write1
else
gosub write0
endif
cmd = cmd << 1 : sck = 0 : nSel = 1 : return

Write0:
SDI = 0 : SCK = 0 : pauseus 16 : SCK = 1 : return

Write1:
SDI = 1 : SCK = 0 : pauseus 16 : SCK = 1 : return


And your issue might've been that you didn't turn the nSEL line on/off before writing out to the port...unless you didn't include that part in the posts.

Ioannis
- 9th June 2008, 14:54
I really wonder if the skimask's example will work because is the same shiftout command.

I have also ordered a couple of the modules (the high power ones) but will be delay and cannot test them right now.

Ioannis

skimask
- 9th June 2008, 16:13
I really wonder if the skimask's example will work because is the same shiftout command.
Ioannis
It is the same...
I think the only difference is toggling the SEL line.

davewanna
- 9th June 2008, 22:50
Skimask,

Thankyou for doing that... From the little bit of C that I have learnt in the last 24 hours, that looks good.

I did try nSEL last night with a couple of code attempts, but I did not try it consistantly.

I did also try to use the SSP last night for a short period. No luck yet, but I didn't expect it to be easy, I haven't used the SSP yet.

I am off to work now, but will try again later today.


Thanks for the assistance.

Ioannis
- 10th June 2008, 07:29
It could be a timing issue. No time to check now. If anyone could?

Ioannis

davewanna
- 10th June 2008, 09:16
I am just getting started on this again tonight...

I the example from Skimask above, the variable CMD is I assume an array of 16 bits? so I should declare..



CMD VAR bit[16]


Am I correct?


My next question is how do I get my word variable into series of 16 bits?



dout = $D0F7

for n = 16 to 0 step -1
CMD[n] = %dout.15
dout << 1
next n





Keep in mind I am almost certain that the above example will not work... It's just to show that I am trying, not just asking everyone else to make my project for me...

Ioannis
- 10th June 2008, 09:38
For this CMD[n] = %dout.15 try as:

CMD.0[n] = %dout.15

Ioannis

davewanna
- 10th June 2008, 10:36
My current attempt is....



cmd var byte[15]
dout = $C0F7

...

lcdout $fe,$C0," "
for n = 16 to 1 step -1
CMD.0[n] = dout.15
dout = dout << 1
lcdout $fe,14,bin dout.15
next n


This results in ... 1000000111101110 ... and I want ... 1100000011110111 ...

i.e. It has chopped the 1 off the start, and added a 0 on the end...

Ioannis
- 10th June 2008, 10:49
Try this

for n = 15 to 0 step -1

Ioannis

davewanna
- 10th June 2008, 11:00
I just realised what I did...

dout = dout << 1
lcdout $fe,14,bin dout.15

I was displaying the dout bit after it had shifted... Stupid me.. I swapped those 2 lines, and I know I am giving the right string of bits to the array...
I'm trying to display the array to see if all those bits went in.. Something is not right tho.



n VAR BYTE
nn VAR BYTE
cmd VAR BYTE[15]
...

lcdout $fe,$C0," "
for n = 15 to 0 step -1
CMD.0[n] = dout.15
lcdout $fe,14,bin dout.15
dout = dout << 1
next n

lcdout $fe,$d4," "
for nn = 0 to 15
lcdout $fe,$14,cmd.0[nn]
next nn



This last bit where I try to display the cmd array just gives me garbage characters.

davewanna
- 10th June 2008, 11:17
I have just read a very in depth post by Melanie about bits in bytes, and arrays etc...
I think I understood most of it.. and have changed my code to....



n VAR BYTE
nn VAR BYTE
cmd VAR BYTE[16]

...

lcdout $fe,$C0," "
for n = 15 to 0 step -1
CMD[n] = dout.15
lcdout $fe,14,bin dout.15
dout = dout << 1
next n

lcdout $fe,$d4," "
for nn = 0 to 15
lcdout $fe,$14,cmd(nn)
next nn


However it still doesn't seem to have solved my problem. I am still getting the wierd character in the bottom lcdout array..

davewanna
- 10th June 2008, 12:42
Latest code is...



Start:

lcdout $fe,$80,"RFM12B OSC test"
lcdout $fe,$94,"1MHz to 10Mhz"

pause 200

dout = $C0F7
gosub Writecmd

goto Start


WriteCMD:

for n = 15 to 0 step -1
CMD[n] = dout.15
dout = dout << 1
next n

lcdout $fe,$c0," "
SCK = 0
nSEL = 0

for nn = 15 to 0 step -1
if cmd[nn] = 1 then
gosub write1
else
gosub write0
endif
lcdout $fe,$14,bin cmd[nn]
next nn

sck = 0
nSel = 1

return


Write0:
SDI = 0 : SCK = 0 : pauseus 16 : SCK = 1 : return


Write1:
SDI = 1 : SCK = 0 : pauseus 16 : SCK = 1 : return


This displays the binary that I want to feed to the RF12 on the LCD... However it still has not changed the osc freq of the RF12. Despite being in a loop giving the command over and over...

davewanna
- 10th June 2008, 13:35
Ok so it is working... As far as I have set all the initialize registers as per the datasheet example with the exception of the 10MHz clock line, and a 4.5V low battery detect...
I can now see the clock line change to 10MHz

Working code is below...



define osc 4

' Define LCD registers and bits
DEFINE LCD_DREG PORTD 'LCD addressing
DEFINE LCD_DBIT 4 'L1 = $80
DEFINE LCD_RSREG PORTD 'L2 = $C0
DEFINE LCD_RSBIT 1 'L3 = $94
DEFINE LCD_EREG PORTD 'L4 = $D4
DEFINE LCD_EBIT 3

lcdrw var portd.2
nSEL var portb.4
SDI var portb.5
SCK var portb.6
SDO var portb.7
dout var word
n VAR BYTE
nn VAR byte
cmd var bit[16]

TRISB = %00000100

low lcdrw
nSEL = 1
SDI = 1
SCK = 0

pause 500

lcdout $fe,1

Start:

lcdout $fe,$80,"RFM12B Initialize"

pause 500

gosub Init

goto start





'Subroutines

WriteCMD:

for n = 15 to 0 step -1
CMD[n] = dout.15
dout = dout << 1
next n

SCK = 0
nSEL = 0

for nn = 15 to 0 step -1
if cmd[nn] = 1 then
gosub write1
else
gosub write0
endif
next nn

sck = 0
nSel = 1
pause 500
return


Write0:
SDI = 0 : SCK = 0 : pauseus 16 : SCK = 1 : pauseus 1 : return


Write1:
SDI = 1 : SCK = 0 : pauseus 16 : SCK = 1 : pauseus 1 : return




Init:

dout = $80D8 'enable register, 433MHz, 12.5pf
gosub Writecmd
dout = $8208 'Turn on crystal, !PA
gosub Writecmd
dout = $A640
gosub Writecmd
dout = $C647
gosub Writecmd
dout = $94C0 'VDI, FAST, 134kHz
gosub Writecmd
dout = $C2AC
gosub Writecmd
dout = $CA80
gosub Writecmd
dout = $CA83 'FIFO8, SYNC
gosub Writecmd
dout = $C49B
gosub Writecmd
dout = $9850 '!mp, 9810=30kHz, Max Out
gosub Writecmd
dout = $E000 'Not Used
gosub Writecmd
dout = $C80E 'Not Used
gosub Writecmd
dout = $C0F7 '10MHZ, 4.5V
gosub Writecmd

Return



This has been very painful for me - having not touched RF before, so I'm hopeing that someone else gets some use out of my hair-pulling...

There is much more to do, I haven't even got round to sending data yet, and I haven't worked out how you are meant to run the pic on the 1MHz clock, then set it to 10Mhz, and have it all work fine. At the moment I have it running on an external 4Mhz crystal.

When I get a good working TX and RX bit of code I will start a new post with as much info as I can work out...

Thanks again to all the people that helped me get this far!

Cheers

Dave

skimask
- 10th June 2008, 13:57
Note to yourself, even though in this case it doesn't matter (because the default OSC is 4), the DEFINE's MUST be in CAPS, either the DEFINE itself, or the parameters, I don't remember which. In my case, I always just put both in caps. Don't have to figure out which one then.


DEFINE OSC 4
DEFINE LCD_DREG PORTD 'LCD addressing
DEFINE LCD_DBIT 4 'L1 = $80
DEFINE LCD_RSREG PORTD 'L2 = $C0
DEFINE LCD_RSBIT 1 'L3 = $94
DEFINE LCD_EREG PORTD 'L4 = $D4
DEFINE LCD_EBIT 3
DEFINE SHIFTPAUSE_US 16
include "modedefs.bas"
lcdrw var portd.2:nsel var portb.4:sdi var portb.5:sck var portb.6:sdo var portb.7
dout var word:trisb=4:lcdrw=0:nsel=1:sdi=1:sck=0:pause 500:lcdout $fe,1
Start:
lcdout $fe,$80,"RFM12B Initialize":pause 500:gosub Init:goto start
WriteCMD:
nsel = 0 : shiftout sdi , sck , 5 , dout\16 : nsel = 1 : return
Init:
for temp = 0 to 12
lookup temp , [ $80D8 , $8208 , $A640 , $C647 , $94C0 , $C2AC , $CA80 , $CA83 , $C49B , $9850 , $E000 , $C80E , $C0F7 ] , dout
gosub writecmd
next temp
Return


This should do exactly the same thing, but uses the shiftout command. Assuming your original program worked as you wanted it to work.... :)

Ioannis
- 10th June 2008, 19:53
Here come the colon again!

Well, Dave, actually you are running the Oscillator of the PIC at 4MHz, but the core is running at 1MHz (/4).

If you want the pic running at 10MHz, then you should capitalize the define on top of the program as Skimask stated like this:

DEFINE OSC 10

After that, compiler will take care of the rest timing.

Ioannis

davewanna
- 10th June 2008, 23:25
Ioannis...

I had defined the osc to 10, the problem was that at startup, the RFM12 only puts out a 1Mhz clock signal, and at that speed, it would not initialize the RFM12... Not exactly sure why.. I did some mucking around last night, and have now got it to start on 1Mhz, write to the LCD first, then initialize the RFM12. I'm not sure why but it did not want to do this the other way around.

I'm glad I am now making progress..

Ioannis
- 11th June 2008, 08:32
Let me understand what you are doing. You are clocking the PIC from a 1MHz RFM12 output?

Ioannis

davewanna
- 11th June 2008, 10:32
on first turn-on, the RFM12 outputs a 1Mhz osc for the pic, which I am putting in on OSC1.
After I write the command ... dout = $C0F7 : gosub Writecmd ... this changes that osc freq from 1MHz to 10MHz... so my pic will run at 10MHz. I have figured out the problem to just be that my crystal setting was on XT, not HS.

I am onto the next part of converting C to Basic if anyone dare to assist....


void WriteFSKbyte( uchar DATA )
{
uchar RGIT=0;
uint temp=0xB800;
temp|=DATA;
Loop: SCK = 0;
nSEL=0;
SDI=0;
SCK=1;
if(SDO) //polling SDO
{
RGIT=1;
}
else
{
RGIT=0;
WriteCMD(temp);
}
}


So far I have...



WriteFSKbyte:
temp = $B800

Loop:
SCK = 0
nSEL = 0
SDI = 0
SCk = 1
if SDO = 1 then
rgit = 1
else
rgit = 0
endif
SCK = 0
SDI = 1
nSEL = 1
if rgit = 0 then
goto loop
endif
rgit = 0
dout = temp
gosub writecmd
return


The part I am struggling with right now is...



uchar RGIT=0;
uint temp=0xB800;
temp|=DATA;


Does this take bit 0 of DATA and stick it to temp somehow??

Someone with some C knowledge please help me! :)

Thanks!

Ioannis
- 11th June 2008, 11:24
Why are you compilcating things?

Just put a crystal or resonator on the pic and have it working on a precise time base. What you are doing now is screwing the timing of the commands and may be locks the pic at the time the freq. is changing. Also at 4MHz and lower the PIC should use XT settings and above that HS settings.

I strongly recommend the PIC using its own clock.

Ioannis

davewanna
- 11th June 2008, 11:31
This is mostly just an excercise for a project of mine. The way the module is toted, is that it can output a clock signal for the pic, so only the crystal on the module is needed, and not 2 seperate oscillators. The examples I have seen of this module being used in the real world have had the osc out line connected to the pic. I have now got this part working though, and it is not a problem.

Cheers

dhouston
- 11th June 2008, 13:58
Do you have to reset the registers each time the RF12 starts or are they non-volatile so that the clock frequency will always be 10MHz (or whatever) once set? In my experience, registers like these are usually non-volatile.

Bruce
- 11th June 2008, 15:49
uchar RGIT=0; unsigned char RGIT = 0
uint temp=0xB800; unsigned int temp = B800 hex
temp|=DATA; temp = temp ORed with the value in DATA

davewanna
- 12th June 2008, 03:16
dhouston,

So far the registeres are reset when the power is removed. i.e. everytime I restart the module, the OSC is back to 1MHz.

I did read in one of the datasheets, but I can't find it now, something along the lines of that you have to reset the module after changing settings for them to be stored. Not 100% sure of this right now. But I haven't had any luck attempting it so far.


Bruce,

What do you mean ORed?

I'm struggling to get the over-all picture of what it sends here.

From my understanding.. to actually send data.. I must use the "WriteCMD loop from earlier, to send first $b8, and then another 8bits of data.

Which I have done by...


WriteFSKbyte:
...
temp = $b8
SCK = 0
SDI = 1
nSEL = 1
dout.highbyte = temp
dout.lowbyte = dat
gosub writecmd


Now currently there is what I presume is a preamble..



dout = $8228 'open PA
gosub writecmd
pauseus 4
dout = $8238 'something to do with transmit register
gosub writecmd
pauseus 2
dat = $AA
gosub writefskbyte 'send $B8AA
gosub writefskbyte 'send $B8AA
dat = $2D
gosub writefskbyte 'send $B82D
dat = $D4
gosub writefskbyte 'send $B8D4

That should actually transmit.... "$AAAA, $2DD4 " ????????

Suffering from severe lack of experience in RF and/or serial transmission, when should I send the pre-amble? Before every transmission?

i.e.
Preamble,
Transmit data, - Several 16bit segments...
Pause
do whatever else..
Preamble
Transmit data
etc??

skimask
- 12th June 2008, 03:31
I did read in one of the datasheets, but I can't find it now, something along the lines of that you have to reset the module after changing settings for them to be stored. Not 100% sure of this right now. But I haven't had any luck attempting it so far.
Don't see anything about that.


What do you mean ORed?
Logical OR'ing. It's in the manual, or look it up on Wiki under Boolean logic.


I must use the "WriteCMD loop from earlier, to send first $b8, and then another 8bits of data.
ShiftIn/ShiftOut will work just fine with this chip, no need to reinvent the wheel as you have been doing.

davewanna
- 12th June 2008, 13:13
I knew I was in above my head, and would do something stupid like try to re-invent the wheel. Oh well, at least I'm learning.. :)

To counter that last statement though, would you believe I took "ORed" as "O-Red" and thought it was some C statement I didn't know about.. Really quite obvious now... stupid me.

I think I will just keep re-inventing the wheel until it's almost as good as the one we got now, then chuck it out and go with the tried and tested. At least by then I might know more about the module, and might have more luck trying the shiftout commands...

Wirecut
- 10th December 2008, 09:08
Hi davewanna

at the end; what about your project.

Are you satisfied about the RFM12.

I have just ordered some of that modules to make some RF trial; as I'm a novice in RF and SPI interfacing.

I have in plan to use this module with a 16F877 just to make experience before use it on a real project.

After reading the full tread, I have now a gray view of the problem that I will encounter to make the RF connection working. From your experience and your point of view, do you have any helpfull suggestions about PBP and the RFM12 module interfacing?

Ciao

Leo

davewanna
- 11th December 2008, 03:05
Leo,

To be honest was never able to reliably transmit data. There were many many errors.

I have since found out a few pieces of information that I have not yet had a chance to try. Firstly, do not use the onboard osc output to run your MCU. Make sure the osc output is switched off. Apparently it creates noise in the transmission.

Also the setup is crucial, there is a lot of trial and error needed to get the settings right.

There is a lot of very valuable information here (http://blog.everythingrobotics.com/tags/rfm12/) Most of the code is in C, but there is a forum, and a lot of information related to setup that you will need.

The datasheets will need to be read many many times.

I have some code that uses PBP's SEROUT commands to communicate with the RFM12, and currently it kind of works. I would suggest you try write it yourself, as you will get a much better understanding, but let me know if you really need it. It might even give me the kick I need to have another look at it.

Cheers

Dave

Wirecut
- 11th December 2008, 16:04
Hi Dave,

I need help about the SPI interface because I never used before.

Looking at the tread is difficult to understand the way to send data to the RFM12. I would understand clearly the PBP way to send data to the RFM12 module.

This evening I will made some trial, following this tread and I will tell you the results.

Thanks in advance for you kindly availability.

Ciao

Leo

Archangel
- 11th December 2008, 18:26
I think I will just keep re-inventing the wheel until it's almost as good as the one we got now, then chuck it out and go with the tried and tested. At least by then I might know more about the . . .

My God, Dave, that's brilliant ! That exactly describes the HUMAN LEARNING PROCESS ! I have never seen it put just that way. I really mean it, Good Job.

iw2fvo
- 24th October 2011, 10:56
Thank for the link.
The program is written in German... but it is a great help.
regards,
Ambrogio

camerart
- 6th October 2016, 10:56
Hi,
Is this thread still open?
I would like to ask questions
Camerart

Art
- 8th October 2016, 10:32
I’m struggling to find C code in this thread that isn’t directly converted to PBP.

C:


temp|=DATA;


PBP:


temp = temp|data'


C falls short of PBP or asm for bitwise operations because you can never access a bit in a single instruction,
hence the bit mask is being used in C to only write the set bits in the temp variable.


B800 = 1011100000000000
DATA = 0000000000001111

B800 | DATA = 1011100000001111



If you’re clocking the external chip, and then feeding the pic it’s output clock that would normally be a very good idea
because once you send the command to PLL the external chip's clock higher, both chips are clocked at the higher speed,
and no timing adjustment of the SPI or SHIFTOUT routines should need adjusting,
EXCEPT for the fact that the pic’s instruction clock is divided by 4, so the two chip’s practical instruction timing is not increased evenly,
so you will need two different communication timings for the two different speeds of the external chip.

Art
- 8th October 2016, 10:36
All of the While loops are the same as PBP While...WEND where the closing brace is WEND.



n=16;
while(n--) {dosomething();}




n=16;
WHILE (n > 0)
gosub dosomething’
n = n - 1'
WEND

Art
- 8th October 2016, 10:44
Nops in this function are a delay. The function (subroutine as you worked out) sends a single zero value out of the SPI bus.
If you were to set SCK low, and then high again in the very next instruction, the receiving device might miss the signal.
Especially if the external device runs slower than the transmitting device. You can use @nop in PBP.

Since there are several flavours of SPI, it would be better to reproduce this then use PBP SHIFTOUT (unless it’s already working).
They are really only turning two port bits on & off.



writezero:
portb.something = 0’ clear SPI data
portb.something = 0’ clear clock pin, the receiver will look at the value of the SPI data bit now.
delay
portb.something = 1’ set the clock pin, the receiver will look for the next bit on the next rising edge (next time you call this function, or the writeone function).




void Write0( void )
{
SDI=0;
SCK=0;
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
NOP();
SCK=1;
NOP();
}