Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 15:52 10 Nov 2025 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 (4)

     Page 1 of 2    
Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 04:33am 24 Feb 2016
Copy link to clipboard 
Print this post

With the implementation of 5.1, a new version of CFGEN and work done by Nathan we have both new facilities available to CFunctions and much easier ways of debugging

This tutorial will use the example of the "TONE " command to explain and explore these new delights

I'll attach the complete MPLabX working project which you can unzip and use as the basis for experimenting. This has the advantage that all of the settings are already done for you. Personally, whenever I start a new development I just copy a complete working project into a new directory and then I know all the key set-up is done and correct. I have all my projects on Dropbox which also has the advantage that if I make a mess of something I can always go back to a previous version and restore it.

2016-02-24_143241_Tutorial-4.zip

So lets start with the key setting we need to make inside MPLabX to use the new features. The project compiler setting just got complicated but it is just a string that needs copying and pasting into the compiler additional options




The full string is:

-fPIC -mno-abicalls -fno-toplevel-reorder -shared -membedded-data -mno-long-calls -fno-jump-tables -mno-jals -mgpopt -Wuninitialized -Wunused-variable -Wunused-value -Wunreachable-code

Remember to make sure the optimisation is set to 1, this is on the dropdown on the "Option categories"

Next we need to set the linker options as follows:




Note that "Exclude Standard Libraries", "Do not link startup code" and "Exclude floating point library" are ticked on the "Libraries" option category.

These are things we only need to do once and if you copy projects as explained above they will already be done for you in the new project.

In all projects we will be using functions defined in CFunctions.c which Geoff distributes with the firmware but is attached here for easy reference:

2016-02-24_143024_CFunctions.zip

Next post we will get started on using the facilities







Edited by matherp 2016-02-25
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 07:00am 24 Feb 2016
Copy link to clipboard 
Print this post

A big issue with Cfunctions is that they end up in a different place in the microprocessor's memory than that which the compiler/linker in MPLabX expected. This means that the code must be completely position independent and any data used must be in the same relative position to the code as when linked and the code must only use relative and not absolute addressing.

Unfortunately the combination of the GCC compiler and MIPS processor isn't very good at this. In the first version of CFGEN there was special code that identified absolute addressing jump instructions in the machine code and replaced them with relative jumps. This solved some of the issues but it still left us unable to use things like "switch" statements (SELECT CASE in Basic) and it was completely impossible to use string literals e.g.

char mystr[]="a string";

Nathan has identified additional compiler options as in the first post that solve the switch issue and also created an elegant mechanism for using static data of all sorts. In order to enable the latter I've worked with TassyJim to update CFGEN to handle static data such that it can be used in the Cfunction. Note static data only works in "Merge" mode. Trying to use static data in "Join" mode will give an error in CFGEN.

Nathan's code to implement static data involves using a special built in compiler function "__builtin_return_address(0)". This function returns the return address of the current function, not one assumed by the linker but the actual return address in the running processor. Using some clever code this allowed Nathan to create a mechanism that enables our CFunction to correct the linker produced addressing for our runtime environment.

The specific issue with constant data is that the compiler/linker puts it in a different section of memory from the code called ".rodata". The code is in ".text"

In a later post I'll explore this in more detail but for the moment we will just use Nathan's code as "boiler-plate" than can just be copied and pasted into our project. All this is in the project I attached earlier.

In previous versions of loadable drivers we had to pass to the driver the address of the CFunction e.g.

sub mm.startup
dim myaddr%=peek(cfunaddr SSD1351)
dim i%=SSD1351(myaddr%,15,30,11,96,1)'Address of CFunction, DCpin, RSTpin, CSpin, vertical resolution, orientation
end sub


Now the CFunction can calculate its own address:

__attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c)
{
*c = (unsigned int) (__builtin_return_address (0) - (b -a)) ;
}
long long main(void){
volatile unsigned int libAddr ;
getFPC(&main,&&getFPCLab,&libAddr) ; // warning can be ignored
getFPCLab: { }
return libAddr;
}


This little program gives exactly the same answer as peek(cfunaddr cfuncname).

Moreover substituting NULL for &main give the offset of the linked version to the in-memory version so setting the function pointers which the Micromite firmware uses is now trivial e.g.

volatile unsigned int libAddr ;
getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored
getFPCLab: { }
DrawRectangleVector= (unsigned int)&DrawRectangle_SSD1351 + libAddr;


Using static data is also extremely easy:

[code]
static const unsigned short _initData[] = {
0x00,0x0001,0x03,0xA8A4,0x0C,0x0000,0x0D,0x080C,0x0E,0x2B00,0x1E,0x00B7,
0x01,0x2B3F,0x02,0x0600,0x10,0x0000,0x11,0x6070,0x05,0x0000,0x06,0x0000,
0x16,0xEF1,0x17,0x0003,0x07,0x0233,0x0B,0x0000,0x0F,0x0000,0x41,0x0000,
0x42,0x0000,0x48,0x0000,0x49,0x013F,0x4A,0x0000,0x4B,0x0000,0x30,0x0707,
0x31,0x0204,0x32,0x0204,0x33,0x0502,0x34,0x0507,0x35,0x0204,0x36,0x0204,
0x37,0x0502,0x3A,0x0302,0x3B,0x0302,0x23,0x0000,0x24,0x0000,0x25,0x8000};
unsigned short * initData = (unsigned short *)((void *)_initData + libAddr )) ;
[/code]

creates an array of shorts (16-bit integers) that can then be subsequently accessed as initData[arrayindex]

Also outputting a string is now similarly easy:


static const char _s1[]="A string to print\r\n";
char *s1 = (unsigned char *)((void *)_s1 + libAddr );
MMPrintString(s1);


Lets now look at the "TONE" Cfunction. I've written some trivial wrappers for Micromite firmware calls that make using "PRINT" statements in the code much easier so debugging becomes more straight-forward


#define debugging
__attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c)
{
*c = (unsigned int) (__builtin_return_address (0) - (b -a)) ;
}

#ifdef debugging
void p_string(const char *s){
volatile unsigned int libAddr ;
getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored
getFPCLab: { }
unsigned char * testData = (unsigned char *)((void *)s + libAddr );
MMPrintString(testData);
}
void p_int(int a,int base){
char b[64];
IntToStr(b,a,base);
MMPrintString(b);
}
void p_float(float a,int m, int n, char c){
// m is the nbr of chars before the decimal point
// n is the nbr chars after the point
// ch is the leading pad char
char b[14];
FloatToStr(b,a, m, n ,c);
MMPrintString(b);
}
#endif


Defining "debugging" includes in the compilation three functions which print strings, integers (base can be 2, 8, 10, 16), and floats

I'm then using these in my code as follows:

#ifdef debugging
static const char crlf[]="\r\n";
static const char s1[]="Clock speed= ";
static const char s2[]="Tick Rate= ";
#endif
if(clockspeed>80000000){ //start by assuming divide by 1
clockspeed/=4;
} else {
clockspeed/=2;
}
tickspersecond=IntToFloat(clockspeed);
tickrate=FDiv(tickspersecond,*frequency);
#ifdef debugging
p_string(s1); p_int(clockspeed,10); p_string(crlf);
p_string(s2); p_float(tickrate,10,1,' '); p_string(crlf);
#endif


So thanks to Nathan we can just include his routines and this allows:

much easier debugging
easy setting of function pointers
simple use of static data of all types

Don't be put off by the apparent complexity of the few lines of code. All they are really doing is creating a mapping from the code/data layout that the compiler/linker assumed and what we have in the CFunction to allow us to access the stored data from within the CFunction.

Next post I will look at the "TONE" code itself and some of the new features in the Micromite firmware that it uses.



Edited by matherp 2016-02-25
 
Justplayin

Guru

Joined: 31/01/2014
Location: United States
Posts: 330
Posted: 11:26am 25 Feb 2016
Copy link to clipboard 
Print this post

Are only the compiler setting changes sufficient to make use of switch/case? I can not seem to get switch/case to work in a CSub. Strangest part is the routine with the switch/case added, results in a smaller CSub.

  matherp said  -fPIC -mno-abicalls -fno-toplevel-reorder -shared -membedded-data -mno-long-calls -fno-jump-tables -mno-jals -mgpopt -Wuninitialized -Wunused-variable -Wunused-value -Wunreachable-code

Remember to make sure the optimisation is set to 1, this is on the dropdown on the "Option categories"

Next we need to set the linker options as follows:

Note that "Exclude Standard Libraries", "Do not link startup code" and "Exclude floating point library" are ticked on the "Libraries" option category.



I am not a Mad Scientist...  It makes me happy inventing new ways to take over the world!!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 10:51pm 25 Feb 2016
Copy link to clipboard 
Print this post

  Quote  Are only the compiler setting changes sufficient to make use of switch/case?


Yes

  Quote  I can not seem to get switch/case to work in a CSub.


Without seeing your code it is difficult to see what is wrong. What are the symptoms? Before the compiler switch changes you would have seen a processor exception. If you are not getting this it is probably just a coding error
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1671
Posted: 04:15am 26 Feb 2016
Copy link to clipboard 
Print this post

Hi Peter,

thanks for helping us to understand CFunctions!


I'm not sure if this is the right place to ask? (I didn't dare to ask )

With the "cfunctions.h" (MMBasic 5.1) we got a lot probably very useful stuff.
But I could not find a description for that. Do you have one (already)? Not all functions (Macros) are self explaining I think.
Some of them are of course self explaining if one knows your very appreciated code examples here.

I guess it would make the CFunctions more popular if the users see what they can do with them ...

Maybe this could be a "CFunctions - learning by example (5)"?

//Macros to call each function.

#define CurrentCpuSpeed (*(unsigned int *) Vector_CurrentCpuSpeed)
#define uSec(a) ((void (*)(unsigned int )) Vector_uSec) (a)
#define putConsole(a) ((void (*)(char)) Vector_putConsole) (a)
#define getConsole() ((int (*)(void)) Vector_getConsole) ()
#define ExtCfg(a,b,c) ((void (*)(int, int, int)) Vector_ExtCfg) (a,b,c)
#define ExtSet(a,b) ((void(*)(int, int)) Vector_ExtSet) (a,b)
#define ExtInp(a) ((int(*)(int)) Vector_ExtInp) (a)
#define PinSetBit(a,b) ((void(*)(int, int)) Vector_PinSetBit) (a,b)
#define PinRead(a) ((int(*)(int)) Vector_PinRead) (a)
#define GetPortAddr(a,b) ((volatile unsigned int * (*)(int,int)) Vector_GetPortAddr) (a,b)
#define GetPinBit(a) ((int (*)(int)) Vector_GetPinBit) (a)
#define MMPrintString(a) ((void (*)(char*)) Vector_MMPrintString) (a)
#define IntToStr(a,b,c) ((void (*)(char *, long long int, unsigned int)) Vector_IntToStr) (a,b,c)
#define FloatToStr(a,b,c,d,e) ((void (*)(char *, float, int, int, char)) Vector_FloatToStr) (a,b,c,d,e)
#define CheckAbort() ((void (*)(void)) Vector_CheckAbort) ()
#define GetMemory(a) ((void* (*)(int)) Vector_GetMemory) (a)
#define GetTempMemory(a) ((void* (*)(int)) Vector_GetTempMemory) (a)
#define FreeMemory(a) ((void (*)(void *)) Vector_FreeMemory) (a)
#define DrawRectangle(a,b,c,d,e) ((void (*)(int,int,int,int,int)) (*(unsigned int *)Vector_DrawRectangle)) (a,b,c,d,e)
#define DrawRectangleVector (*(unsigned int *)Vector_DrawRectangle)
#define DrawBitmap(a,b,c,d,e,f,g,h) ((void (*)(int,int,int,int,int,int,int, char*)) (*(unsigned int *)Vector_DrawBitmap)) (a,b,c,d,e,f,g,h)
#define DrawBitmapVector (*(unsigned int *)Vector_DrawBitmap)
#define DrawLine(a,b,c,d,e,f) ((void (*)(int,int,int,int,int,int)) Vector_DrawLine) (a,b,c,d,e,f)
#define FontTable (void*)((int*)(Vector_FontTable))
#define FMul(a,b) ((float (*)(float, float)) Vector_FMul) (a,b)
#define FAdd(a,b) ((float (*)(float, float)) Vector_FAdd) (a,b)
#define FSub(a,b) ((float (*)(float, float)) Vector_FSub) (a,b)
#define FDiv(a,b) ((float (*)(float, float)) Vector_FDiv) (a,b)
#define FCmp(a,b) ((int (*)(float, float)) Vector_FCmp) (a,b)
#define FSin(a) ((float (*)(float)) Vector_FSin) (a)
#define FLog(a) ((float (*)(float)) Vector_FLog) (a)
#define FPow(a,b) ((float (*)(float, float)) Vector_FPow) (a,b)
#define atanf(a) ((float (*)(float)) Vector_atanf) (a)
#define FloatToInt(a) ((long long (*)(float)) Vector_FloatToInt) (a)
#define IntToFloat(a) ((float (*)(long long)) Vector_IntToFloat) (a)
#define CFuncmSec (*(unsigned int *) Vector_CFuncmSec)
#define ExtCurrentConfig ((int *) Vector_ExtCurrentConfig)
#define CFuncRam ((int *) Vector_CFuncRam)
#define Option ({struct option_s *optionstructurepointer; optionstructurepointer=(void *)(unsigned int)Vector_Option;})
#define HRes (*(unsigned int *) Vector_HRes)
#define VRes (*(unsigned int *) Vector_VRes)
#define LoadFloat(a) ((float (*)(unsigned int)) Vector_LoadFloat) (a)
#define SoftReset() ((void (*)(void)) Vector_SoftReset) ()
#define CFuncInt (*(unsigned int *) Vector_CFuncInt)
#define ScrollLCD(a) ((void (*)(int)) (*(unsigned int *)Vector_ScrollLCD)) (a)
#define CFuncT1 (*(unsigned int *) Vector_CFuncT1)
#define CFuncT5 (*(unsigned int *) Vector_CFuncT5)

//
// Useful macros
//
#define DrawPixel(x, y, c) DrawRectangle(x, y, x, y, c)
#define PIC32MX470F512H_DEVID 0x0580A053
#define PIC32MX470F512L_DEVID 0x0580B053
#define PIC32MX170F256B_DEVID 0x06610053
#define PIC32MX270F256B_DEVID 0x06600053
#define PIC32MX170F256D_DEVID 0x0661A053
#define PIC32MX270F256D_DEVID 0x0660A053
#define DEVID (*(volatile unsigned int *)0xBF80F220)
#define HAS_28PINS ((DEVID & 0xfffffff) == PIC32MX170F256B_DEVID || (DEVID & 0xfffffff) == PIC32MX270F256B_DEVID)
#define HAS_44PINS ((DEVID & 0xfffffff) == PIC32MX170F256D_DEVID || (DEVID & 0xfffffff) == PIC32MX270F256D_DEVID)
#define HAS_64PINS (DEVID & 0xfffffff) == PIC32MX470F512H_DEVID
#define HAS_100PINS (DEVID & 0xfffffff) == PIC32MX470F512L_DEVID
#define NBRPINS ({int j=28; if(HAS_44PINS)j=44; if(HAS_64PINS)j=64;if


Regards
Michael
causality ≠ correlation ≠ coincidence
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 4126
Posted: 04:25am 26 Feb 2016
Copy link to clipboard 
Print this post

I expect you'd understand better if you have the source code from Geoff.

Then you can see the underlying code each of those lets you call.

It's a lot easier to browse to get the flavour of it if you're not planning to change it or write lots of it.

BTW, for anyone who finds C tough: this way of using it is about as cryptic as is possible without going to deliberate extremes! But it's more or less forced as a result of wanting to marry Basic code to C code without (for example) just adding the C to Geoff's code.

Conccentrate on the left part of those #defines, not the horrid stuff.

(deliberate extremes... hmm... like IOCCC)

JohnEdited by JohnS 2016-02-27
 
Justplayin

Guru

Joined: 31/01/2014
Location: United States
Posts: 330
Posted: 09:19am 26 Feb 2016
Copy link to clipboard 
Print this post

Thanks Peter,

It helped knowing switch/case would work with no other modifications or additions other than the compiler options. Found the error to be a bad for loop which created an unreachable code condition. The compiler simply ignored all the code contained in the loop which explains the resulting smaller CSub.

--Curtis
I am not a Mad Scientist...  It makes me happy inventing new ways to take over the world!!
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 06:24am 27 Feb 2016
Copy link to clipboard 
Print this post

Per the request above, before I continue with the exploration of the "TONE" CFunction, I will try and do a very brief overview of all of the facilities in Cfunctions.h. John is, however, correct in his post above. The best way to understand this is to download the Micromite 5.1 source from MMBasic.com. Load it into MPLabX and then you can use "find in projects" in the "edit" menu to understand properly how things work. Geoff's code is well commented and beautifully structured so easy to interpret.

Basically there are 3 categories of interfaces to the Micromite firmware available

1. Micromite firmware routines that can be called from CFunctions

2. Definitions that allow CFunctions to use Micromite firmware data

3. Definitions that allow the Micromite firmware to call CFunction routines

The latter is the most complex so I'll leave that to last:

Micromite firmware routines that can be called from CFunctions

General calls

int a= CurrentCpuSpeed; // returns the CPU speed in Hz

uSec(a); // waits for "a" microseconds (automatically compensates for CPU speed)

putConsole('x'); // writes a single character (char) to the console

char a= getConsole(); //reads a single character from the console, returns -1 if no character in buffer

ExtCfg(pin, mode, options); //SETPIN command, valid parameters are given at the bottom of Cfunctions.h

ExtSet(pin, value); //checks if a pin is a digital output and sets it to "value" if it is

int a=ExtInp(pin);//Get the value of an I/O pin and returns it. For digital returns 0 if low or 1 if high. For analog returns the reading as a 10 bit number with 0b1111111111 = 3.3V

PinSetBit(pin,IOreg); //sets the pin's bit in the specified I/O register. The list of IO registers is given as the second last section in CFunctions.h. Can be used for many functions e.g. PinSetBit(pin,LATCLR) == PIN(pin)=0

int a=PinRead(pin); //returns the value of a digital input pin (0 or 1)

int a=GetPortAddr(pin, IOreg); //gets the hardware register address of the defined register for the pin specified. Used to implement fastest possible I/O from a CFunction

int a = GetPinBit(pin); //gets the position in the I/O register of the pin. Use 1 << GetPinBit(pin) to get a mask for that pin

MMPrintString(s); //Prints on the console the string pointed to by "s" (e.g. char s[10]). Can only be used with string literals by using the technique identified in the earlier post in this thread

char a[10]; IntToStr(a,number,base); // converts the integer "number" to a string in "a" using base "base"

char a[10]; char padchar=' '; FloatToStr(a,number,charsbefore,charsafter,padchar) ; //converts the floating point number "number" to a string in "a" with "charsbefore" characters before the decimal point and "charsafter" after it. Pads leading array elements with the char padchar.

CheckAbort(); //returns to the command prompt if ctrl-C pressed, otherwise processing in the CFunction continues

char *p=GetMemory(nbytes);// gets some Basic memory for use in the Cfunction. The memory is available all the time the program is running. In the example the memory can then be accessed as an array p[0] to p[255]

char *p=GetTempMemory(nbytes);// gets some Basic memory for use in the Cfunction. The memory is returned when the Cfunction exits even though the program may stay running. In the example the memory can then be accessed as an array p[0] to p[255]

FreeMemory(p);//returns the memory pointed to by "p" to Basic

SoftReset();// Executes a reset of the Micromite

TFT drawing calls

DrawRectangle(x1, y1, x2, y2, colour);//Draws a rectangle between x1,y1 and x2,y2 filled in colour. NB DrawPixel is the same as DrawRectangle where x1,y1=x2,y2 and is defined as a macro. Take care when using this as, for example, DrawPixel(X++,Y++, colour) will have unintended consequences

DrawBitmap(x1, y1, width, height, scale, fc, bc, bitmap );// Draws a bitmap starting at x1,y1 of width and height specified, scaled by scale with bit set to 1 in colour "fc" and bits set to 0 in colour "bc".

DrawLine(x1, y1, x2, y2, width, colour);// Draws a line between x1,y1 and x2,y2 of the "width" specified and in the "colour" specified

int a = HRes;// returns the horizontal resolution of the screen

int a = VRes;// returns the vertical resolution of the screen

Floating point calls

float c=FMul(a,b);//multiplies a and b
float c=FAdd(a,b);//adds a and b
float c=FSub(a,b);//subtracts b from a
float c=FDiv(a,b);//divides a by b

int c = FCmp(a,b);//returns 1 if a>b, returns 0 if a=b, returns -1 if b>a

float c=FSin(a);// returns sine of angle "a" specified in radians

float c=FLog(a);// returns the natural logarithm of "a"

float c=FPow(a,b);// a raised to the power b

float c = atanf(a);// returns atan(a)

int c = FloatToInt(a);// returns "a" converted to an integer

float c = IntToFloat(a); //returns "a" converted to a float

float c = LoadFloat(0xnnnnnnnn); //converts the hex representation of a float into c.

Definitions that allow CFunctions to use Micromite firmware data

datatypeofelement c = Option->element;// Used to read an element from the Option structure
Option->element= a;// Used to write an element in the Option structure

The option structure is given in CFunctions.h. It can be read and written from within a Cfunction. Great care should be taken if writing to understand the implications. It is used extensively in loadable drivers to save and read the pin numbers for CS, CD RESET etc. The Option structure supports the various Basic OPTION commands

int c=ExtCurrentConfig[pin]; //Used to interrogate the current I/O mode of a pin. Valid entries are given at the bottom of CFunction.h

int c=CFuncRam[n];// CFuncRam is a 256 byte (64 integers/floats or 32 long long) area of memory exclusively reserved for CFunctions . It is not changed by RUN unlike Basic memory so can be used for storing information that subroutines in a CFunction may need that has been set up by an initialisation routine run, for example, by MMSTARTUP. If you want to use it as an array of a datatype other than integers use the syntax:

char *p = (void*)CFuncRam; char c=p[n];// allow access to CFuncRam as a char array

Definitions that allow the Micromite firmware to call CFunction routines

This is the most complex set of CFunction mechanisms but also the most powerful. In order to use these we need to know where in memory the CFunction is located. This can be done by passing into the Cfunction its address obtained by:

address = peek(CFunAddr, cfunctionname)

and then adding an offset from the main CFunction to the function to be called by the Micromite firmware. Alternatively, the boiler plate presented in the posts above does all this for you:


void routinetobecalled(void){
}

__attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c)
{
*c = (unsigned int) (__builtin_return_address (0) - (b -a)) ;
}

long long main(void){
volatile unsigned int libAddr ;
getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored
getFPCLab: { }
MMFirmwarevector=(unsigned int)&routinetobecalled + libAddr;
}


Given this the actual function pointers we can set are as follows:

TFT drawing calls

Specifying just two CFunction routines allows us to implement a loadable driver for any type of display.

void MyDrawRectangle(int x1, int y1, int x2, int y2, int c)
DrawRectangleVector=(unsigned int)&MyDrawRectangle + libAddr;

This sets the function that the Micromite firmware will call if trying to draw a rectangle. The calling sequence must be exactly as specified.

void MyDrawBitmap(int x1, int y1, int width, int height, int scale, int fc, int bc, unsigned char *bitmap )
DrawBitMapVector=(unsigned int)&MyDrawBitmap + libAddr;

This sets the function that the Micromite firmware will call if trying to draw a bitmap (e.g.TEXT). The calling sequence must be exactly as specified.

There is one additional routine that may be specifed in a driver if you wish to use the display as a console device:
void MyScrollLCD(int lines)
ScrollLCD=(unsigned int)&MyScrollLCD + libAddr;
I haven't attempted to use this so far

Timer/repeat calls

The next set of vectors allow Cfunction routines to be called repeatedly

void TickInt(void) //called every millisecond in the MM firmware clock routine
CFuncmSec=(unsigned int)&TickInt + libAddr;

void BasicInt(void) //called after every Basic Statement
CFuncInt=(unsigned int)&BasicInt + libAddr;

void Timer1Int(void) //called by the timer1 interrupt routine
// The main CFunction is responsible for setting up the timer to interrupt as required. NB using this interrupt is incompatible with also using IR
CFuncT1=(unsigned int)&Timer1Int + libAddr;

void Timer5Int(void) //called by the timer5 interrupt routine
// The main CFunction is responsible for setting up the timer to interrupt as required. NB using this interrupt is only available on the MMPlus
CFuncT5=(unsigned int)&Timer5Int + libAddr;


Edited by matherp 2016-02-28
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1671
Posted: 04:05pm 27 Feb 2016
Copy link to clipboard 
Print this post

Hi Peter,

thanks a lot for this post!

That's what I dreamed of! I think one of the big advantage of Geoff's Basic is the
comprehensive documentation. Sometimes it's better to have something and you know what you have in your warehouse instead of only to have something and not to know.

Maybe we (I?) could convert your text into a PDF and include it in the MM distribution package? Later then we (I myself or other TBS members) can expand it with some (of your TBS-) code snippets and/or links to working CFunctions. But I don't want to scare you!

Yes, I can imagine your efforts!

BTW, is this a typo?
int c=CFuncRam[n];// CFuncRam is a 256 byte (64 integers/floats or 32 long long)

64 x 32 != 256.

Sorry for interfering this tutorial.

Thanks again!
Michael
causality ≠ correlation ≠ coincidence
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 10:23pm 27 Feb 2016
Copy link to clipboard 
Print this post

  Quote  Maybe we (I?) could convert your text into a PDF


Please do

  Quote  BTW, is this a typo?
int c=CFuncRam[n];// CFuncRam is a 256 byte (64 integers/floats or 32 long long)


No. An "int" is 4 bytes long as is a "float", a "long long" is 8 bytes long so 256/4=64 and 256/8=32

would read better as:

int c=CFuncRam[n];// CFuncRam is a 256 byte array (64 integers/floats or 32 long long)
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1671
Posted: 06:35am 28 Feb 2016
Copy link to clipboard 
Print this post

  matherp said  
  Quote  Maybe we (I?) could convert your text into a PDF


Please do


A first draft:
EDIT: CFunctions_Documentation_V02.PDF/RTF:
2016-02-29_074945_CFunctions_Documentation_V02.zip


  matherp said  No. An "int" is 4 bytes long as is a "float", a "long long" is 8 bytes long so 256/4=64 and 256/8=32

would read better as:

int c=CFuncRam[n];// CFuncRam is a 256 byte array (64 integers/floats or 32 long long)

oops, too early in the morning. Sorry, you are right, of course!

MichaelEdited by twofingers 2016-03-01
causality ≠ correlation ≠ coincidence
 
paceman
Guru

Joined: 07/10/2011
Location: Australia
Posts: 1329
Posted: 05:42pm 28 Feb 2016
Copy link to clipboard 
Print this post

@ Michael
The link above doesn't work.
 
MicroBlocks

Guru

Joined: 12/05/2012
Location: Thailand
Posts: 2209
Posted: 06:55pm 28 Feb 2016
Copy link to clipboard 
Print this post

http://www.thebackshed.com/forum/uploads/twofingers/2016-02-28_151650_CFunctions_Documentation_V01.zip
Microblocks. Build with logic.
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1671
Posted: 10:00pm 28 Feb 2016
Copy link to clipboard 
Print this post

Thanks Greg, thanks Jean!

I fixed the link and added a RTF with some minor corrections.

Regards
Michael
causality ≠ correlation ≠ coincidence
 
erbp
Senior Member

Joined: 03/05/2016
Location: Australia
Posts: 195
Posted: 12:30am 14 May 2016
Copy link to clipboard 
Print this post

Hi,

I think this might be a question for matherp, but others may also know the answer.

I am a newbie to PIC32 programming (although not programming per se) and to this forum and am trying to get an understanding of how to program CFunctions. I have downloaded the two zip files from the first post in this thread (the Tutorial-4 and the CFunctions.h zips). I have also installed MPLAB X IDE v3.26 and MPLAB XC32 C Compiler v1.40 (free version) from the Microchip website.

I was following through the steps in the first post and located the various settings referenced - all were set as described. I then tried to do a build (Run | Build Project menu selections) and got the following errors reported. Not being familiar with this compiler I am not sure what it is trying to tell me. It looks like the 2nd, 3rd and 4th errors are just flow-on from the first which appears to be the real culprit. I see in the listed set of compiler options there is a "-mno-abicalls" (this is in accordance with the notes in the first post) whereas the error seems to be saying that "-mabicalls" is needed.




Have I done something wrong? Has the compiler changed since the first post in this thread or what?


Thanks, Phil.

 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 12:41am 14 May 2016
Copy link to clipboard 
Print this post

Phil

Geoff and I both use compiler version 1.33. Please try that and then see if it solves the problem.

Please find attached the complete project for the SDcard routines which definitely works on 1.33

2016-05-14_111424_FileRead.zip Edited by matherp 2016-05-15
 
erbp
Senior Member

Joined: 03/05/2016
Location: Australia
Posts: 195
Posted: 01:28am 14 May 2016
Copy link to clipboard 
Print this post

Peter,

I installed compiler v1.33 and Tutorial-4 builds successfully now.

Thanks!
Phil.
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1671
Posted: 06:50am 08 Jul 2017
Copy link to clipboard 
Print this post

@Peter

How can I return a string from a Cfunction to MMBasic?

It seems in the regular way it won't work?
If I understand Cfunctions right I can only return integers (long long).

If I modify (e.g. shorten) a string (out[]) in the Cfunction will MMBasic that accept?

long long strrev(unsigned char in[], unsigned char out[])


Can you maybe post a working example (e.g. ltrim(), split(), ...) which modifies a string?


A second questiton:
Can I call a MMBasic function from within a Cfunction?

Thanks in advance!
Michael



causality ≠ correlation ≠ coincidence
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10565
Posted: 10:54am 08 Jul 2017
Copy link to clipboard 
Print this post

  Quote  Can you maybe post a working example (e.g. ltrim(), split(), ...) which modifies a string?


The attached taken from the SPI flash routines shows you how to return a string


char *readpage(long long *address, long long *cspin){
int k,add,pin=*cspin;
int m;
char *d; //pointer to string to return
d=GetTempMemory(256); //get temp memory for the return string
add=*address<<8; //convert page number to byte number
SPIBRG=1;
PinSetBit(pin,LATCLR);
SPIsend(readdata);
SPIsend((add>>16) & 0xFF);
SPIsend((add>>8) & 0xFF);
SPIsend(add & 0xFF);
for(k=0;k<256;k++){
SPIread(m);
d[k]=m;
}
PinSetBit(pin,LATSET);
return d; //return the pointer
}


  Quote  Can I call a MMBasic function from within a Cfunction?


I think so but I haven't tried and have no examples - one for GeoffEdited by matherp 2017-07-09
 
twofingers

Guru

Joined: 02/06/2014
Location: Germany
Posts: 1671
Posted: 11:25am 08 Jul 2017
Copy link to clipboard 
Print this post

Thanks Peter!

This means I can dynamically allocate memory in a CFunction?! That's not what I've expected. I'll try that.

Best regards
Michael

edit:
I understand: The allocated memory will be freed when/after the CFunction is terminated!
a$=cfunc_trim(examplestr$)

ie only a$ allocates (MMBasic)memory!

I was confused by this (in C Routines.pdf p.3):
  Quote  A CFunction normally returns a value which is a 64-bit integer (a long long int in C) however this can be changed (see Typed Parameters later).

... and did not read the "Typed Parameters" section ... Edited by twofingers 2017-07-09
causality ≠ correlation ≠ coincidence
 
     Page 1 of 2    
Print this page
The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025