Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 16:41 28 Apr 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : CFunctions - learning by example

     Page 1 of 2    
Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 03:45am 16 Sep 2015
Copy link to clipboard 
Print this post

As 4.7 gets ever nearer to being ready for full release, I thought it might be worth writing a few words about how to write CFunctions and hopefully dispelling some of the "fear" of C development.

The first thing to understand is that because CFunctions work within the Micromite environment all the difficult stuff normally associated with programming a microprocessor from scratch in C is already done for you. The code in a CFunction is called from Basic and returns to Basic and the interface between the two is really very simple.

So first things first; we need to install a development environment for coding in C. To do this download and install MPLabX for your environment.

Assuming you install MPLabX V3.10 you can accept the installation defaults until you get to the screen titled "Completing the MPLAB X IDE v3.10 Setup Wizard" untick all of these options and then press finish.

Then you need to install a compiler. To maintain compatibility with the Micromite firmware we are going to use XC32 v1.33. This is available on the Downloads Archive tab of the same page accessed before.

Again accept the defaults during installation except on the screen Compiler Settings. Here it is essential you tick the option to add XC32 to the PATH environment variable.

Finally, to convert the code from MPLabX to be usable in the Micromite we need to convert the binary file output by the compiler into text. Download the conversion program from here

Assuming that all works properly you will then have the (completely free) environment you need to write CFunctions.

To make things really easy, I have attached a sample MPLabX project

2015-09-16_132206_CFunctions.X.zip

Download and extract the zip file to your development directory and there should be a complete MPLabX project ready to start work.

Run MPLabX. From the file menu select "Open Project". Browse to the directory containing the extracted sample "CFunction.X" and click "Open Project"

This should open various windows, on the left hand side is the project browser, the main panel is the source code for the sample.

Click on the hammer icon with the brush and the output window will display build progress and hopefully a message saying "Build Successful"

Next to get the code usable in Basic we need to run CFGen.exe which will immediately open a window to allow selection of the binary file. Browse to the top level CFunctions project directory, then "dist"/"default"/"production". You should then see a file "CFunctions.X.production.elf", select this file and click open. Next select where you want the Basic version saved and enter a filename for the .BAS file. Click "Save".

This example file is a set of individual CFunctions so we now select "Join" to create an output file with each of them individually included. The use of "Merge" will be explained in a subsequent post.

As well as outputting the Basic file the cfgen program puts the output onto the clipboard so you can paste it direct into, for example, MMEdit.

You have now created your first CFunction.

In the next posts, I will explain the code in the example project, but I would appreciate hearing from you if you get this far successfully
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9063
Posted: 04:03am 16 Sep 2015
Copy link to clipboard 
Print this post

Is there any way to select the XC32 compiler on IDE 2.0 already installed?

...or do I have to download v3 as indicated and follow my nose there?

EDIT: I have downloaded XC32 v1.33, and will install this. Is it as simple as directing MPLAB X IDE to use this as the compiler in the options menus? Will see if I can make it jive.Edited by Grogster 2015-09-17
Smoke makes things work. When the smoke gets out, it stops!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 04:08am 16 Sep 2015
Copy link to clipboard 
Print this post

Assuming that everything explained in the first post worked OK you have a working CFunction development environment but before proceeding there are a couple of gotchas that need to be understood.

When a CFunction is loaded into the Micromite we don't know where in memory it is going to end up so the code has to be capable of running without making assumptions about its position in memory - it must be position independent.

Unfortunately the MIPS instruction set as used by the GCC compiler (XC32 is actually the GCC open source compiler rebadged by Microchip) doesn't produce true position independent code.

Open the example project and in the "projects" tab, right click on CFunctions. Select the last option "properties"

Note that in the Compiler toolchain window you should see XC32 (V1.33) highlighted. If it isn't select it. (Grogster )

Then click on "xc32-gcc" under "XC32(Global Options)" in the additional options you will see the text:

"-fPIC -mno-abicalls"

This is essential and tells the compiler to create, as far as possible, position independent code.

In the "Option Categories" dropdown select "Optimization". The optimization level is set to 1. This is also essential for a CFunction to work.

If you want to create a Cfunction project from scratch it is essential that both the above are set the same.

Given the above setup we can successfully write CFunctions but there are limitations as we will see later when discussing doing any floating point arithmetic in a CFunction
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9063
Posted: 04:33am 16 Sep 2015
Copy link to clipboard 
Print this post

I get no such options under xc32-gcc:




Smoke makes things work. When the smoke gets out, it stops!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 04:40am 16 Sep 2015
Copy link to clipboard 
Print this post

What do you get when you click on "Conf:[default]"?
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9063
Posted: 04:46am 16 Sep 2015
Copy link to clipboard 
Print this post

This is what I get:




Smoke makes things work. When the smoke gets out, it stops!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 04:51am 16 Sep 2015
Copy link to clipboard 
Print this post

That looks OK. Does the project compile (hammer with brush)? Which version of MPLabX are you running? if less than 2.26 then upgrade.
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9063
Posted: 04:51am 16 Sep 2015
Copy link to clipboard 
Print this post

I have managed to open the C file, or one of them anyway! (there's a few files inside the project - not familiar with X files.)

MPLAB is very sluggish - is this normal?
It does open the project, but it takes ages, and navigating around the application is sluggish - like working on Windows 95 again.....

Quad-core 2GHz machine with 8GB RAM and Win8.1 x64, if that helps.

What's with the red line down the middle of the code window?





EDIT: Version 2.0. I will upgrade as you suggest. Will be tomorrow - it's 2:53AM, and I should be resting my brain, not stressing it out!

Somewhat anticlimactic, yes, but goodnight - will follow up with this tomorrow. Edited by Grogster 2015-09-17
Smoke makes things work. When the smoke gets out, it stops!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 05:02am 16 Sep 2015
Copy link to clipboard 
Print this post

The redline is just a right margin indicator, can be turned off.

Tools->Options->Fonts&Colors->Highlighting->Text Limit Line then set the color to white (or whatever your background color is).

Definitely upgrade to a later version.

Can't comment on performance on your machine. I always buy ex-company Dell Workstations, currently dual processor T5500 (8 x 3.47G + hyperthreading), 22GB RAM, USD300

 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 07:15am 16 Sep 2015
Copy link to clipboard 
Print this post

Hopefully you now have a development environment up and running so we can start to develop something.

Why are CFunctions useful?

From my perspective, there are two main reasons to use them which to a great extent overlap:

First, they can increase processing speed for things that are time critical
Second, they allow control of I/O in a more flexible way than may be possible in Basic.

The display drivers are an obvious example. It is possible to code one in Basic but it would be too slow to be useful.

As an example CFunction rather than just repeat things that have been done before I'm going to develop in this thread a simple function generator and hopefully show the sources of information that provide the information needed.

The PIC32 can generate a comparator reference, basically a very simple DAC, which can be used in various ways.
See page 215 of the manual for the PIC32MX1xx chips

This all looks a bit complicated but there is a Microchip library that makes handling the various peripherals on the chip fairly easy. This is called plib and has its own reference manual which happily includes some example code for the comparator reference on page 305.

To turn this into a CFunction we need to make a few changes:


long long triangleout(long long *count){
unsigned int step;
unsigned int loop;
unsigned int ramp;
unsigned int i=*count;
while(i){
i--;
for ( loop =0; loop <= 15; loop ++){
for ( ramp = 0; ramp <= 31; ramp ++){
if ( ramp <= 15 ){
// ramp up
step = ramp;
} else {
// ramp down
step = 31 - ramp;
}
CVREFOpen( CVREF_ENABLE | CVREF_OUTPUT_ENABLE | CVREF_RANGE_LOW| CVREF_SOURCE_AVDD | step );
}
}
CVREFClose(); // Disable CVREF (not executed)
}
return 0;
}


Cut and paste this into the CFUnctions.c example file immediately after the statement #include "cfunctions.h". You will see the previous line is "#include <plib.h>" this is the statement that tells the compiler to access the plib library to provide the routines we need to compile successfully.

You should then find you can press the hammer to re-compile, press "join" in cfgen (assuming it is still open) and the new code is ready to paste into Basic.

Looking at the C code, the first line says that my new CFunction will be called triangleout and can be translated into Basic as:

function triangleout(count as integer) as integer. Note that "long long" in C is the equivalent of "INTEGER" in Micromite Basic, i.e. a 64-bit signed number

The "{" symbol marks the beginning of the code in the function and is paired with the last "}" which is equivalent to END FUNCTION

In general "{" and "}" are used to replace all paired Basic statements such as IF/ENDIF, FOR/NEXT and DO/LOOP.
";" is used to terminate a statement rather than the end-of-line in Basic, and note that C is case sensitive "Hello" is not the same as "hello".

There are lots of C primers around which I won't try and duplicate, rather I will concentrate on the syntax needed to create a working CFunction.

The really important thing to note in the function declaration is that the input count is defined as "*count". In normal Basic terms this means the data is passed by reference rather than by value. What this means is that "count" is actually the location in memory of the information rather than the information itself.

*count says get the information that is in the location count. In C terms this is a pointer but for our purposes we can just use "*count" whenever we want to use the information.

The next four lines are the equivalent of LOCAL INTEGER ramp etc. except that rather than being 64-bit signed integers they are 32-bit unsigned integers. 32-bits is the size of the hardware registers in the PIC, hence PIC32MX, so arithmetic on 32-bit numbers will be more efficient than 64.

"while(i)" = DO WHILE i>0
"i++;" = i=i+1
"for ( loop =0; loop <= 15; loop ++){" = FOR loop = 0 TO 15 STEP 1
"for ( ramp = 0; ramp <= 31; ramp ++){" = FOR ramp = 0 to 31 STEP 1
"if ( ramp <= 15 ){" = IF ramp <= 15 THEN

all very easy and obvious, just remember the "}" symbols substitute for "NEXT", "LOOP" , "ENDIF" etc.

the next line

CVREFOpen( CVREF_ENABLE | CVREF_OUTPUT_ENABLE | CVREF_RANGE_LOW| CVREF_SOURCE_AVDD | step );


uses the plib library call to enable the comparator reference, enable the output and set the range to 0-0.677 of AVDD and specify which step in the potential divider to use.

Once the count in the while loop finishes then "CVREFClose();" turns off the reference voltage

"return 0" = triangleout=0

From the PIC32 manual we can see that the reference voltage will be output on pin 25 of a 28-pin MX170 and pin 14 of a 44-pin part. Page 23 of the MX470 manual says the reference will be on pin 23 of a MM+ (untested)

My test program looks like this:


dim i%=triangleout(1000000)
end

CFunction triangleout
00000000
8C8A0000 8C8B0004 014B1025 50400021 00001021 3C06BF81 24050020 2407001F
24090010 3C0FBF81 340E8000 254CFFFF 018A502B 256BFFFF 014B6821 01805021
01A05821 1000000B 00004021 00E21823 0044180B 34638060 ACC39800 24420001
1445FFFA 2C440010 25080001 11090004 00000000 00001021 1000FFF6 00001821
ADEE9804 018D6025 1580FFE9 254CFFFF 00001021 03E00008 00001821
End CFunction



On my Scope I get a rough triangle waveform of 2V amplitude and frequency of 212KHz





Next post I will look at setting a specific frequency for the output
Edited by matherp 2015-09-17
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8582
Posted: 09:00am 16 Sep 2015
Copy link to clipboard 
Print this post

The previous version of the code just ran for an arbitrary amount of time - 1000000*15 waveforms.

It would be much better to tell it how long to run for. To do this we need to use a timer. We don't have access to the TIMER variable directly inside a CFunction but using a couple of tricks we can do what we want.

We need a couple of magic instructions to do this. These don't change so can be copied and pasted whenever we need to time something. They use the PIC32MX chips internal core timer. This is a 32-bit register that is updated at half the clock speed. So if we set "CPU 40" it will be updated 20,000,000 times a second.

The core timer register is not accessible in the same way as normal I/O register which is simply mapped as a memory address. However, we can include into our C code a couple of in-line assembler instructions to set and read the core timer


asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the core timer to the value of current_ticks (i.e. 0)
while(current_ticks<time_in_ticks){
asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the value of the core timer and put it into current_ticks


So we now have a way of measuring time in core timer ticks. However to convert this into real time we need to know the current clock speed. This is done by using one of the macros in the cfunctions.h header file


unsigned int tick_rate;
tick_rate = CurrentCpuSpeed /2; // Get the number of core clock timck per second


Putting this all together we can easily do something (or nothing) for a defined number of seconds:


long long pausesecs(long long *time_in_seconds){
unsigned int time_in_ticks;
unsigned int tick_rate;
tick_rate = CurrentCpuSpeed /2; //get the number of clock ticks per second
time_in_ticks=*time_in_seconds * tick_rate;
unsigned int current_ticks = 0;
asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0
while(current_ticks<time_in_ticks){
asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed
}
return CurrentCpuSpeed;
}



Note that this code also returns the CPU speed to Basic. You can copy this code into cfunctions.c and try it. My test program is:


dim i%=pausesecs(4)
print "CPU Speed is ",i%, " Hz"
end
CFunction pausesecs
00000000
3C029D00 8C420000 8C420000 00021842 8C840000 70641802 00002021 40844800
0083202B 50800007 00001821 40044800 0083202B 1480FFFD 00000000 03E00008
00001821 03E00008 00000000
End CFunction


Taking this mechanism we can change triangleout to run for a set number of seconds


long long triangleout(long long *time){
unsigned int step;
unsigned int ramp;
unsigned int time_in_ticks;
unsigned int tick_rate;
tick_rate = CurrentCpuSpeed /2; //get the number of clock ticks per second
time_in_ticks=*time * tick_rate;
unsigned int current_ticks = 0;
asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0
while(current_ticks<time_in_ticks){
asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed
for ( ramp = 0; ramp <= 31; ramp ++){
if ( ramp <= 15 ){
// ramp up
step = ramp;
} else {
// ramp down
step = 31 - ramp;
}
CVREFOpen( CVREF_ENABLE | CVREF_OUTPUT_ENABLE | CVREF_RANGE_LOW| CVREF_SOURCE_AVDD | step );
}
}
CVREFClose(); // Disable CVREF (not executed)
return CurrentCpuSpeed;
}


Replace the earlier version in cfunctions.c with this code, recompile and test it .

My version is:


dim i%=triangleout(10) ' run for 10 seconds
print i% ' CPU speed
end
CFunction triangleout
00000000
3C029D00 8C420000 8C420000 00021042 8C830000 70431002 00001821 40834800
0062182B 50600013 34038000 3C07BF81 24060020 2408001F 40094800 00001821
10000003 00002021 01032023 0065200B 34848060 ACE49800 24630001 1466FFFA
2C650010 0122182B 1460FFF3 00000000 34038000 3C02BF81 AC439804 3C029D00
8C420000 8C420000 03E00008 00001821
End CFunction

 
vegipete

Guru

Joined: 29/01/2013
Location: Canada
Posts: 1082
Posted: 09:36am 16 Sep 2015
Copy link to clipboard 
Print this post

If I read that right, at 20M ticks per sec (CPU 40), the 32 bit core timer will overflow in about 3 1/2 minutes. Are unsigned ints passed back to Basic as expected?
Visit Vegipete's *Mite Library for cool programs.
 
kiiid

Guru

Joined: 11/05/2013
Location: United Kingdom
Posts: 671
Posted: 09:46am 16 Sep 2015
Copy link to clipboard 
Print this post

@Peter, I think you can write a small ebook about this. Not everyone reads TBS ;)

Apart from this, I would like to calm down the excitement about the CFunctions. MMbasic has gradually started evolving towards a C-shell, which in some sense is good, but not necessarily the right way to go especially for someone who is just starting. I would suggest, let's not get too extreme in that direction because it will kill the general simplicity of the mite.

http://rittle.org

--------------
 
atmega8

Guru

Joined: 19/11/2013
Location: Germany
Posts: 712
Posted: 11:08am 16 Sep 2015
Copy link to clipboard 
Print this post

@kiid,

i aggree to your statement.
I see the Danger that the simplicity of MMBASIC can be enterd by the C.
On the other side cFunctions are not a "Must" and they expand the possibilities of MMBASIC, and it stays to be an Interpreter.

No advantage without disadvantage 😉
 
Chris Roper
Senior Member

Joined: 19/05/2015
Location: South Africa
Posts: 280
Posted: 11:42am 16 Sep 2015
Copy link to clipboard 
Print this post

90% of MMBasic users are not going to go anywhere near C Functions but at least 75% of them will ask for drivers for new hardware or Functions that are not part of the core. Many of them may well give up and move on because the Micromite couldn't do what they needed.

CFunctions, and now the Library capability, allow the 10% that will use them to contribute to extending MMBasic, be it to contribute drivers to handle new hardware or to add new functions to the language.

That takes a load off Geoff, gives others the satisfaction of being able to contribute to a good cause, and builds the user base by retaining the new users that may have given up.

I see it as a Win/Win for all rather than a pending problem for new users.

Cheers
Chris

http://caroper.blogspot.com/
 
Zonker

Guru

Joined: 18/08/2012
Location: United States
Posts: 761
Posted: 12:57pm 16 Sep 2015
Copy link to clipboard 
Print this post

I think the C Functions are awesome..! Most of the time not needed, but, if you need to go screaming fast or need access to special hardware inside... It IS the answer..! And, I like the fact that now a new "insider" group can create, maintain, and offer ready to rock add-on's for the everyone to enjoy... ONLY at TBS does this kind of goodness happen...
 
paceman
Guru

Joined: 07/10/2011
Location: Australia
Posts: 1327
Posted: 04:35pm 16 Sep 2015
Copy link to clipboard 
Print this post

  Chris Roper said   90% of MMBasic users are not going to go anywhere near C Functions but at least 75% of them will ask for drivers for new hardware or Functions that are not part of the core. Many of them may well give up and move on because the Micromite couldn't do what they needed.

CFunctions, and now the Library capability, allow the 10% that will use them to contribute to extending MMBasic, be it to contribute drivers to handle new hardware or to add new functions to the language.

That takes a load off Geoff, gives others the satisfaction of being able to contribute to a good cause, and builds the user base by retaining the new users that may have given up.
I see it as a Win/Win for all rather than a pending problem for new users.


I totally agree with this.

Greg
 
paceman
Guru

Joined: 07/10/2011
Location: Australia
Posts: 1327
Posted: 04:36pm 16 Sep 2015
Copy link to clipboard 
Print this post

Edit: Removed - double post.
GregEdited by paceman 2015-09-18
 
B15HOP
Newbie

Joined: 11/09/2015
Location: Australia
Posts: 15
Posted: 09:21pm 16 Sep 2015
Copy link to clipboard 
Print this post

  kiiid said   @Peter, I think you can write a small ebook about this. Not everyone reads TBS ;)

Apart from this, I would like to calm down the excitement about the CFunctions. MMbasic has gradually started evolving towards a C-shell, which in some sense is good, but not necessarily the right way to go especially for someone who is just starting. I would suggest, let's not get too extreme in that direction because it will kill the general simplicity of the mite.


You're exactly right. The two languages should be seperate. Basic is basic and C is for intermediate or advanced programmers. I learnt how to code as a kid in Basic. C is a far more powerful language designed to get more performance out of your code but it comes at the cost of requiring more brainpower and programming experience. I'm actually better at C, but that's because I have been using C for 15 years.
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9063
Posted: 09:58pm 16 Sep 2015
Copy link to clipboard 
Print this post

Oh goody. Another C expert to help us write Cfunctions then.
We're always gonna need those C experts. I've just started playing with the very basics(pardon the pun!) of C, and I am lost at the moment!
Smoke makes things work. When the smoke gets out, it stops!
 
     Page 1 of 2    
Print this page
© JAQ Software 2024