Author: P. Mather
With the implementation of 5.1, a new version of CFGEN and work done by Nathan [on TBS] 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.
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:
/*******************************************************************************************
*
* Definitions used when calling MMBasic Interpreter API Functions from CFunctions
*
*
******************************************************************************************/
//Addresses in the API Table for the pointers to each function
#define Vector_CurrentCpuSpeed (*(int*)0x9D000000) //CurrentCpuSpeed
#define Vector_uSec (*(int*)0x9D000004) //void uSec(unsigned int us)
#define Vector_putConsole (*(int*)0x9D000008) //void putConsole(int c)
#define Vector_getConsole (*(int*)0x9D00000C) //int getConsole(void)
#define Vector_ExtCfg (*(int*)0x9D000010) //void ExtCfg(int pin, int cfg, int option)
#define Vector_ExtSet (*(int*)0x9D000014) //void ExtSet(int pin, int val)
#define Vector_ExtInp (*(int*)0x9D000018) //int ExtInp(int pin)
#define Vector_PinSetBit (*(int*)0x9D00001C) //void PinSetBit(int pin, unsigned int offset)
#define Vector_PinRead (*(int*)0x9D000020) //int PinRead(int pin)
#define Vector_GetPortAddr (*(int*)0x9D000024) //unsigned int *GetPortAddr(int pin, unsigned int offset)
#define Vector_GetPinBit (*(int*)0x9D000028) //int GetPinBit(int pin)
#define Vector_MMPrintString (*(int*)0x9D00002C) //void MMPrintString(char* s)
#define Vector_IntToStr (*(int*)0x9D000030) //void IntToStr(char *strr, long long int nbr, unsigned int base)
#define Vector_FloatToStr (*(int*)0x9D000034) //void FloatToStr(char *p, float f, int m, int n, char ch)
#define Vector_CheckAbort (*(int*)0x9D000038) //void CheckAbort(void)
#define Vector_GetMemory (*(int*)0x9D00003C) //void *GetMemory(size_t msize);
#define Vector_GetTempMemory (*(int*)0x9D000040) //void *GetTempMemory(int NbrBytes)
#define Vector_FreeMemory (*(int*)0x9D000044) //void FreeMemory(void *addr)
#define Vector_DrawRectangle *(unsigned int *)(int*)0x9D000048 //void DrawRectangle(int x1, int y1, int x2, int y2, int c)
#define Vector_DrawBitmap *(unsigned int *)(int*)0x9D00004C //void DrawBitmap(int x1, int y1, int width, int height, int scale, int fg, int bg, unsigned char *bitmap )
#define Vector_DrawLine (*(int*)0x9D000050) //void DrawLine(int x1, int y1, int x2, int y2, int w, int c)
#define Vector_FontTable (*(int*)0x9D000054) //const unsigned char *FontTable[FONT_NBR]
#define Vector_FMul (*(int*)0x9D000058) //float FMul(float a, float b)
#define Vector_FAdd (*(int*)0x9D00005C) //float FAdd(float a, float b)
#define Vector_FSub (*(int*)0x9D000060) //float FSub(float a, float b)
#define Vector_FDiv (*(int*)0x9D000064) //float FDiv(float a, float b)
#define Vector_FCmp (*(int*)0x9D000068) //int FCmp(float a,float b)
#define Vector_FSin (*(int*)0x9D00006C) //float sinf(float)
#define Vector_FLog (*(int*)0x9D000070) //float logf(float)
#define Vector_FPow (*(int*)0x9D000074) //float powf(float, float)
#define Vector_atanf (*(int*)0x9D000078) //float atanf(float)
#define Vector_FloatToInt (*(int*)0x9D00007C) //long long int FloatToInt(float)
#define Vector_IntToFloat (*(int*)0x9D000080) //float IntToFloat(long long int a)
#define Vector_CFuncmSec (*(int*)0x9D000084) //CFuncmSec
#define Vector_ExtCurrentConfig (*(int*)0x9D000088) //int ExtCurrentConfig[NBRPINS + 1];
#define Vector_CFuncRam (*(int*)0x9D00008C) //StartOfCFuncRAM
#define Vector_Option (*(int*)0x9D000090) //Option
#define Vector_HRes (*(int*)0x9D000094) //HRes
#define Vector_VRes (*(int*)0x9D000098) //VRes
#define Vector_LoadFloat (*(int*)0x9D00009C) //float LoadFloat(unsigned long)
#define Vector_SoftReset (*(int*)0x9D0000A0) //void SoftReset(void)
#define Vector_CFuncInt (*(int*)0x9D0000A4) //CFuncInt
#define Vector_ScrollLCD *(unsigned int *)(int*)0x9D0000A8 //void scrollLCD
#define Vector_CFuncT1 (*(int*)0x9D0000AC) //CFuncT1
#define Vector_CFuncT5 (*(int*)0x9D0000B0) //CFuncT5
//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(HAS_100PINS)j=100; j ;})
//
//***************************************************************************************************
//
// Constants and definitions copied from the um2 and MM+ source
//
//The Option structure
struct option_s {
char Autorun;
char Tab;
char Invert;
char Listcase;
char Height;
char Width;
int PIN;
int Baudrate;
int ColourCode;
// display related
char DISPLAY_TYPE;
char DISPLAY_ORIENTATION;
// touch related
char TOUCH_CS;
char TOUCH_IRQ;
char TOUCH_SWAPXY;
int TOUCH_XZERO;
int TOUCH_YZERO;
float TOUCH_XSCALE;
float TOUCH_YSCALE;
// for the SPI LCDs
char LCD_CD;
char LCD_CS;
char LCD_Reset;
// Note the next options are only applicable to the MM+
char SerialCon;
char SDCARD_CS;
char SD_CD;
char SD_WP;
char DISPLAY_BL;
char DISPLAY_CONSOLE;
char DefaultFont;
char KeyboardConfig;
char TOUCH_Click;
// To enable older CFunctions to run any new options *MUST* be added at the end of the list
unsigned int ProgFlashSize; // used to store the size of the program flash (also start of the LIBRARY code)
char pins[8]; // general use storage for CFunctions that need to record the pins for a parallel port
};
// Define the offsets from the PORT address
// these are used by GetPortAddr(a,b)
#define ANSEL -8
#define ANSELCLR -7
#define ANSELSET -6
#define ANSELINV -5
#define TRIS -4
#define TRISCLR -3
#define TRISSET -2
#define TRISINV -1
#define PORT 0
#define PORTCLR 1
#define PORTSET 2
#define PORTINV 3
#define LAT 4
#define LATCLR 5
#define LATSET 6
#define LATINV 7
#define ODC 8
#define ODCCLR 9
#define ODCSET 10
#define ODCINV 11
#define CNPU 12
#define CNPUCLR 13
#define CNPUSET 14
#define CNPUINV 15
#define CNPD 16
#define CNPDCLR 17
#define CNPDSET 18
#define CNPDINV 19
#define CNCON 20
#define CNCONCLR 21
#define CNCONSET 22
#define CNCONINV 23
#define CNEN 24
#define CNENCLR 25
#define CNENSET 26
#define CNENINV 27
#define CNSTAT 28
#define CNSTATCLR 29
#define CNSTATSET 30
#define CNSTATINV 31
// configurations for an I/O pin
// these are used by ExtCfg(a,b,c)
#define EXT_NOT_CONFIG 0 //Not Configured
#define EXT_ANA_IN 1 //Analogue Input
#define EXT_DIG_IN 2 //Digital Input
#define EXT_FREQ_IN 3 //Frequency Measurement Input
#define EXT_PER_IN 4 //Period measurement Input
#define EXT_CNT_IN 5 //Pulse Counting Input
#define EXT_INT_HI 6 //Generate Interrupt on RISING edge
#define EXT_INT_LO 7 //Generate Interrupt on FALLING edge
#define EXT_DIG_OUT 8 //Digital Output
#define EXT_OC_OUT 9 //Digital Output with Open Collector
#define EXT_INT_BOTH 10 //Generate Interrupt on BOTH Falling and Rising edges
#define EXT_RESERVED 100 // this pin is reserved and SETPIN and PIN cannot be used
#define EXT_BOOT_RESERVED 101 // this pin is reserved at boot
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.
1 2 3 4 |
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:
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 )) ;
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.
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;
Leave a Reply