Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 13:53 12 May 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 2 of 2    
Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8601
Posted: 10:35pm 16 Sep 2015
Copy link to clipboard 
Print this post

Looking at the above posts, I'm not sure if I should continue the thread or not.
The idea was to progressively develop something using the CFunction capability that couldn't be done in Basic and in the process use more of the fantastic facilities that 4.7 gives for CFunction development with each iteration.

Views?
 
Chris Roper
Senior Member

Joined: 19/05/2015
Location: South Africa
Posts: 280
Posted: 10:59pm 16 Sep 2015
Copy link to clipboard 
Print this post

Please continue, it is an extremely valuable resource that you were building and an excellent contribution.

I apologise for being complicit in the derailing of your thread and sincerely do look forward to working through your tutorial.

Cheers
Chris

http://caroper.blogspot.com/
 
Grogster

Admin Group

Joined: 31/12/2012
Location: New Zealand
Posts: 9073
Posted: 11:03pm 16 Sep 2015
Copy link to clipboard 
Print this post

I 2nd that. Please continue. I plan to follow along, once I download the newer MPLAB X, which I hope to do tonight.

Cfunction are a choice - you don't HAVE to learn byte one of a Cfunction, to be able to use MMBASIC just fine. But if you choose to learn about Cfunctions, threads like yours will be of much use to many.
Smoke makes things work. When the smoke gets out, it stops!
 
kiiid

Guru

Joined: 11/05/2013
Location: United Kingdom
Posts: 671
Posted: 12:07am 17 Sep 2015
Copy link to clipboard 
Print this post

Yes, please continue. I only expressed concerns about the future. Right now the Cfunctions are very useful

http://rittle.org

--------------
 
disco4now

Guru

Joined: 18/12/2014
Location: Australia
Posts: 844
Posted: 12:53am 17 Sep 2015
Copy link to clipboard 
Print this post

Yes continue from me as well.
Regards
Gerry
Latest F4 Latest H7
 
paceman
Guru

Joined: 07/10/2011
Location: Australia
Posts: 1328
Posted: 01:29am 17 Sep 2015
Copy link to clipboard 
Print this post

Same from me.

Greg
 
WhiteWizzard
Guru

Joined: 05/04/2013
Location: United Kingdom
Posts: 2794
Posted: 01:44am 17 Sep 2015
Copy link to clipboard 
Print this post

Lots of CFunction coders being created then

Keep posting Peter - will be great to see a simple-to-follow online tutorial
For everything Micromite visit micromite.org

Direct Email: whitewizzard@micromite.o
 
Frank N. Furter
Guru

Joined: 28/05/2012
Location: Germany
Posts: 815
Posted: 02:02am 17 Sep 2015
Copy link to clipboard 
Print this post

YES PLEASE!!!

I am lost in space, ...äh, lost in C!!!

Please keep on and many THANKS!!!!

Frank
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8601
Posted: 02:59am 17 Sep 2015
Copy link to clipboard 
Print this post

Thanks for the support guys

This post we will look at more of the Micromite internal functions that Geoff has made available to CFunctions. These are all listed in cfunctions.h

The current program runs for a time in seconds determined by the input parameter to the CFunction. However, later we will want to control the frequency of the waveform and whilst it would be possible to use the core timer for both purposes. It would be nice to dedicate it to helping drive the frequency calculations.

So we need a way of telling the CFunction to stop running and to return to Basic.

One way we can do this is to check if the user has entered CTRL-C on the keyboard, the same mechanism we would use to stop any program.


long long triangleout(void){
unsigned int step;
unsigned int ramp;
while(1){
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 );
}
CheckAbort();
}


In this version of the code we are calling the function CheckAbort each time round the loop. If a control C has been pressed the firmware will automatically stop the program running and return control to the Basic prompt - how easy is that?

Note that in this version we now have no parameters to the CFunction so we tell the compiler this by using the keyword "void" in between the brackets.

Also note that we have created a never ending loop by using the construct


while(1){
}

As in Basic the value zero means false and anything else means true.
As we are now using a never ending loop which can only be stopped by pressing CTRL-C there is now no way for the program to tidy up the way it did before by turning off the constant voltage reference - not good

So another way to terminate the loop would be to test for an input changing state:

long long triangleout(long long *pin){
unsigned int step;
unsigned int ramp;
ExtCfg(*pin,EXT_DIG_IN,CNPUSET); //set the pin specified as an input with a pull up
while(PinRead(*pin)){
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;
}


In this version we pass a pin number to the function in "long long *pin"
The call "ExtCfg(*pin,EXT_DIG_IN,CNPUSET);" sets the pin we have specified as a digital input and adds an option to enable the pullup. CNPUSET is defined in cfunctions.h, we could also have used CNPDSET to use a pull down. CNPUCLR and CNPDCLR can be used to remove the pullup/down. These codes actually represent the hardware registers that are used to control the behaviour of the processor pins; see page 127 onwards of the processor manual

ExtCfg is of course the code that the Micromite firmware uses to execute SETPIN.
So we could have used ExtCfg(*pin,EXT_ANA_IN,0) to set the pin to an analogue input or ExtCfg(*pin,EXT_DIG_OUT,0) to set it as a digital output. The list of possible codes is at the bottom of cfunctions.h

To test the state of the pin we use "PinRead(*pin)" which returns 1 or 0 depending on the state of the pin. We can use PinRead because we know the input is a digital input. There is a more flexible version "ExtInp(*pin)" which will also work if the pin is set as an analogue input. In this case it returns a 10-bit integer number representing the 10-bit ADC value.

So our test routine for this version is:


dim i%=triangleout(9) ' CFunction will run until pin 9 goes low
end
CFunction triangleout
00000000
27BDFFD8 3C029D00 AFB00010 8C420010 00808021 8C840000 AFB40020 AFB3001C
AFB20018 AFB10014 AFBF0024 24050002 0040F809 2406000E 3C149D00 3C12BF81
24110020 2413001F 8E820020 0040F809 8E040000 10400015 00001021 00001821
34638060 24420001 2C440010 AE439800 5051FFF6 8E820020 1480FFF9 00401821
02621823 34638060 24420001 2C440010 AE439800 5051FFED 8E820020 5080FFF9
02621823 1000FFEE 00401821 3C029D00 8C420000 3C03BF81 34048000 8FBF0024
AC649804 8C420000 00001821 8FB40020 8FB3001C 8FB20018 8FB10014 8FB00010
03E00008 27BD0028
End CFunction




Edited by matherp 2015-09-18
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1138
Posted: 03:12am 17 Sep 2015
Copy link to clipboard 
Print this post

The sticking point (for me) is NOT the C-language!

Just for learning purposes it might be helpful for BASIC users to have a C / C ++ interpreter as "CH" of www.softintegration.com (the free student version).

But I think the main obstacle why we don't have more CFunction coders is because you need much more informations about the PIC32 hardware (and the internal structures of MMBasic too). It would be helpful to have more examples as C sources.

How/where can I find informations about the meaning of
asm volatile("mtc0 %0, $9": "+r"(current_ticks));

??

Are there dokumentations or books for this? Is e.g. Lucio Di Jasios "Programming 32-bit Microcontrollers" a good starting point?

BTW there is already a excellent step by step tutorial for CFunction (made by G8JCF).

@matherp: thanks for your efforts (all of them)!

My 2c
Michael
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8601
Posted: 03:52am 17 Sep 2015
Copy link to clipboard 
Print this post

  Quote  It would be helpful to have more examples as C sources.


If you look at my posts I've included the C source for nearly everything I've written. If there is anything missing you would like let me know and I'll post it.

  Quote  But I think the main obstacle why we don't have more CFunction coders is because you need much more informations about the PIC32 hardware (and the internal structures of MMBasic too).


The only way to get into the hardware is to get stuck into the various Microchip manuals and look at the various examples. Geoff uses the Microchip plib library as the main interface to the hardware, I've referenced the plib manual in a previous post. The basic hardware design of the PIC32 is exactly the same as all the smaller PICs. Hardware is controlled by registers, you read and write to the registers and they affect the way the hardware works.

The code for MMBasic is freely available, it is beautifully written and commented, download it and get stuck in. I'm afraid there is no easy way other than quite a bit of study.

  Quote  How/where can I find informations about the meaning of

The PIC32 processor has a co-processor that does critical things like exception handling (and controlling the core timer). There are specific instructions that are only used to work with the co-processor. plib has routines to do this but they won't work in a CFunction (I assume a position independence issue). These two lines use the standard GCC syntax for including in-line assembler into the C code. I just treat them as "magic"; use them as is and they will do what you want. These are the only "magic" lines I will or have ever needed to use. If you really want to know more google "mtc0" and follow the various links, it is all there.Edited by matherp 2015-09-18
 
twofingers
Guru

Joined: 02/06/2014
Location: Germany
Posts: 1138
Posted: 04:29am 17 Sep 2015
Copy link to clipboard 
Print this post

  matherp said  
  Quote  It would be helpful to have more examples as C sources.


If you look at my posts I've included the C source for nearly everything I've written. If there is anything missing you would like let me know and I'll post it.


I hope you don't misinterpreted my post as criticism. Your examples (and your "drivers" too) are always appreciated as I hopefully already expressed.

Maybe we should convert (after cleaning) this thread to PDF if it is completed?Edited by twofingers 2015-09-18
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 07:18am 17 Sep 2015
Copy link to clipboard 
Print this post

Here is a link to Microchips online training material.
http://microchip.wikidot.com/training:start
It has lots of information and also a whole section about C in the context of Microchip.
I have been going through the material for a while now and it is really a good resource.


Microblocks. Build with logic.
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8601
Posted: 08:35am 17 Sep 2015
Copy link to clipboard 
Print this post

As I suggested above, a useful addition to our program would be to be able to control the frequency of the output of our "DAC" and this version does that by adding a second parameter to the CFunction call.
This is also a good time to talk about debugging




CFunction triangleout
00000000
27BDFFB0 AFBF004C AFB70048 AFB60044 AFB50040 AFB4003C AFB30038 AFB20034
AFB10030 AFB0002C 0080B021 3C129D00 8E420000 8C570000 0017B842 8CA20000
00021140 02E2001B 004001F4 00008012 2610FFFC 2402000D A3A20024 2402000A
A3A20025 A3A00026 2402000A AFA20010 8E420030 27A40018 02003021 0040F809
00003821 8E42002C 0040F809 27A40018 8E42002C 0040F809 27A40024 00008821
40914800 8E420010 8EC40000 24050002 0040F809 2406000E 3C159D00 3C12BF81
24130020 1000000E 2414001F 40024800 0050102B 1440FFFD 00000000 40914800
2C640010 02831023 0064100B 34428060 AE429800 24630001 1473FFF4 00000000
8EA20020 0040F809 8EC40000 1440FFEF 00001821 34038000 3C02BF81 AC439804
02E01021 00001821 8FBF004C 8FB70048 8FB60044 8FB50040 8FB4003C 8FB30038
8FB20034 8FB10030 8FB0002C 03E00008 27BD0050
End CFunction




long long triangleout(long long *pin, long long *freq){
char b[10];
char crlf[3];
unsigned int step;
unsigned int ramp;
unsigned int samplefreq=*freq * 32; //frequency at which each step must be changed
unsigned int tick_rate;
unsigned int frigfactor=4;
tick_rate = CurrentCpuSpeed /2; //get the number of clock ticks per second
unsigned int dwell=tick_rate/samplefreq - frigfactor;
unsigned int zero=0 ;
unsigned int current_ticks;
crlf[0]=13;
crlf[1]=10;
crlf[2]=0;
IntToStr(b,dwell,10);
MMPrintString(b);
MMPrintString(crlf);
asm volatile("mtc0 %0, $9": "+r"(zero)); //set the current number of ticks to 0
ExtCfg(*pin,EXT_DIG_IN,CNPUSET); //set the pin specified as an input with a pull up
while(PinRead(*pin)){
for ( ramp = 0; ramp <= 31; ramp ++){
do{
asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed
}while(current_ticks<dwell);
asm volatile("mtc0 %0, $9": "+r"(zero)); //set the current number of ticks to 0
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 tick_rate;
}


So we have added a second parameter to the function call "long long *freq" which we will use to tell the CFunction the frequency of the waveform.

Using the code we saw earlier in the thread we calculate the tick rate of our clock:
"tick_rate = CurrentCpuSpeed /2; //get the number of clock ticks per second "
We then need to do a calculation to work out how many clock ticks must occur between each change in the output in order to get our desired frequency. We know than we output 32 different levels for each complete waveform so if we multiply 32 by the desired frequency we get the frequency at which we need to change the output
"unsigned int samplefreq=*freq * 32; //frequency at which each step must be "
If we divide this into the number of ticks per second that should give us the number of ticks to hold each output level. However, we will use some machine cycles repeatedly reading the clock until it has changed by the desired amount so we had better add a frig factor to allow for this:
"unsigned int dwell=tick_rate/samplefreq - frigfactor;"

This looks like the correct sort of calculation and we can tune the frig factor assuming the basic equation is correct but it would be good to have some diagnostics to help in debugging.

There are two basic ways of doing this. First we can return any values of interest in the function return. In the code above we return the tick_rate and print that out from Basic. Alternatively we can use one of the firmware routines to output to the console "MMPrintString".

As the name suggest this outputs a string. In Basic a string is a character array with the length of the string in character zero. In C a string uses a zero character to mark the end of the string.

However, before we can output anything we need to convert the binary representation of the number to a string. We do this Using another firmware function "IntToStr" which takes as its arguments the character array, the number to be converted and the base we want for the conversion.

So we have declared a character array b set up with size 10 "char b[10];" and an array crlf with size 3. We can then use IntToStr to convert the value in "dwell" to a string and load the ascii values for a carriage return and line feed into crlf (remembering to set the third character to zero)

We can then call MMPrintString to output to the console and confirm that the value in dwell looks sensible.

This is debugging in the old style

Note that you cannot use the MPLabX debugging facilities to step through the code for Cfunctions - it cannot work as they are not at the address the compiler thinks they are (position independence again!)

The final bit of this version is to use our timing mechanism to wait in each loop for the correct amount of time


do{
asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed
}while(current_ticks<dwell);
asm volatile("mtc0 %0, $9": "+r"(zero)); //set the current number of ticks to 0


Note that C has almost exactly the same syntax as Basic for a "DO...WHILE test" loop.

Executing the loop takes a few machine cycles so the core timer will actually be greater than dwell by the time we reset it to zero. This is where the frig factor come in to tune the loop. 4 seems to work reasonably well in this program.

So with this version we have a working function generator outputting a triangle wave at a specified frequency - not bad!

Edited by matherp 2015-09-19
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8601
Posted: 01:44am 18 Sep 2015
Copy link to clipboard 
Print this post

One final iteration of this program. It would be good to display other waveforms so we could include square wave, sine wave, saw-tooth, as well as triangle.





DIM sinewave%(31)=( 8,9,10,12,13,14,14,15,15,15,14,14,13,12,10,9,7,6,5,4,2,1,1,0,0,0,1,1,2,3,5,6)
dim i%=funcgen(9,5000,"Sine",sinewave%()) 'pin number for the stop key, frequency output
pause 1000
i%=funcgen(9,5000,"Triangle")
pause 1000
i%=funcgen(9,5000,"Saw")
pause 1000
i%=funcgen(9,5000,"Square")
end
CFunction funcgen
00000000
27BDFFC0 AFBF003C AFBE0038 AFB70034 AFB60030 AFB5002C AFB40028 AFB30024
AFB20020 AFB1001C AFB00018 0080B821 00C08021 00E0F021 3C029D00 8C430000
8C630000 00031842 AFA30014 8CB50000 0015A940 0075001B 02A001F4 0000A812
00008812 00001821 40834800 8C420010 8C840000 24050002 0040F809 2406000E
3C169D00 24120053 3C13BF81 24140020 00151140 10000023 AFA20010 40024800
0043102B 1440FFFD 00000000 82020001 10520006 00711821 10470004 2C860010
01041023 10000010 0086100B 82060002 50C9000D 8CA20000 14CA0003 00000000
10000009 8CA20000 10CB0004 2C820010 14CC0005 00041042 2C820010 00003021
01A2300B 00C01021 34428060 AE629800 24840001 1494FFE1 24A50008 8FA20010
02A2A821 8EC20020 0040F809 8EE40000 1040000B 03C02821 02A01821 00002021
24090049 240A0069 240B0051 240D000F 240C0071 24070073 1000FFD0 2408001F
34038000 3C02BF81 AC439804 8FA20014 00001821 8FBF003C 8FBE0038 8FB70034
8FB60030 8FB5002C 8FB40028 8FB30024 8FB20020 8FB1001C 8FB00018 03E00008
27BD0040
End CFunction



long long funcgen(long long *pin, long long *freq, char mode[], long long sinewave[]){
char b[10];
char crlf[3];
unsigned int step;
unsigned int ramp;
unsigned int samplefreq=*freq * 32; //frequency at which each step must be changed
unsigned int tick_rate;
unsigned int frigfactor=0;
tick_rate = CurrentCpuSpeed /2; //get the number of clock ticks per second
unsigned int dwell=tick_rate/samplefreq - frigfactor;
unsigned int next_clock=dwell;
unsigned int current_ticks=0;
crlf[0]=13;
crlf[1]=10;
crlf[2]=0;
asm volatile("mtc0 %0, $9": "+r"(current_ticks)); //set the current number of ticks to 0
ExtCfg(*pin,EXT_DIG_IN,CNPUSET); //set the pin specified as an input with a pull up
while(PinRead(*pin)){
for ( ramp = 0; ramp <= 31; ramp ++){
do{
asm volatile("mfc0 %0, $9" : "=r"(current_ticks));//get the time in ticks since zeroed
}while(current_ticks<next_clock);
next_clock+=dwell;
if(mode[1]!=83 && mode[1]!=115){ //mode doesn't start with an "S" or "s"
if ( ramp <= 15 ){
// ramp up
step = ramp;
} else {
// ramp down
step = 31 - ramp;
}
} else if (mode[2]==73 || mode[2]==105) { //sIne wave
step=sinewave[ramp];
} else if (mode[2]==81 || mode[2]==113){ //sQuare wave
step = ramp >15 ? 0 : 15;
} else { //must be saw tooth
step=ramp/2;
}
CVREFOpen( CVREF_ENABLE | CVREF_OUTPUT_ENABLE | CVREF_RANGE_LOW| CVREF_SOURCE_AVDD | step );
}
}
CVREFClose(); // Disable CVREF (not executed)
return tick_rate;
}



This allows the demonstration of passing strings and arrays to the CFunction. First we need to pass information about which mode to use. This is done in the array "mode". Note that in C we use square brackets for the array index. C arrays always start at position zero. The CFunction has no information about how big the array is unless we pass the information as a separate parameter. In this case we know that a Basic string will have its size in location "mode[0]" if we wanted to use it.
The CFunction does some very simple parsing of the string looking for upper or lower case characters
If it starts with a "T" or "t" it must be a triangle
otherwise look at the second character which must be a "Q" or "q" for a square wave, "I" or "i" for a sine wave and the code assumes anything else must have been a Sawtooth.

Note the syntax of the C "if" statements:
"==" means is the same as
"!=" means not the same as
"||" means logical OR
"&&" means logical AND

This is one of the biggest areas for error in beginners with C. if(a=b) does not do what you would expect. It actually says assign the value in b to a and then see if a is true or false, perfectly valid but probably not what we usually want- BEWARE!!!

There is one statement in the code which does not have a direct parallel in Basic but which I really like:

step = ramp >15 ? 0 : 15;

This can be translated as: "If ramp>15 then: step=0 : else : step=15 : endif

The other new parameter to the renamed function "funcgen" is "long long sinewave[]". This is simply an integer array and you can see in the Basic code this has been pre-loaded with the amplitude values for a sine wave using 32 points and a 4-bit DAC

These number were generated by a great little excel spreadsheet I found:

2015-09-18_114049_Dac.zip

To use the Basic program just run it and you will get the sinewave on the output pin. Briefly (less than one second, hence the pause) ground pin 9 and it will step on to the next output.

Hopefully, this thread has shown the development of a simple CFunction that does something that couldn't done in Basic but might even be useful.

When my fingers have recovered I'll start a new thread and look at the use of floating point numbers in CFunctions




Edited by matherp 2015-09-19
 
Chris Roper
Senior Member

Joined: 19/05/2015
Location: South Africa
Posts: 280
Posted: 02:04am 18 Sep 2015
Copy link to clipboard 
Print this post

Excellent Tutorial, thanks.

Also a very useful piece of code, I was wondering if the DAC could be used for other than a Voltage reference, you have answered that too :)

I Look forward to the next edition.

Cheers
Chris

http://caroper.blogspot.com/
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 8601
Posted: 10:32pm 18 Sep 2015
Copy link to clipboard 
Print this post

Just one point about the output from CVREF (DAC). It is very high impedance so I use a BUF634 to create a 50ohm output.

The BUF634 is a great device if a little pricey. It is a unity gain opamp with up to 250ma drive capability in a 5-pin TO220 style package. Only 4 pins need to be used, VCC, GND, Vin, Vout, and it works happily on a single 5V supply. It even survives a continuous short-circuited output.

To use the function generator at audio frequencies, you can improve the waveforms by feeding the buffer opamp via a 10k resistor with a 220pF capacitor across the input and ground





Edited by matherp 2015-09-20
 
     Page 2 of 2    
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024