DROlabel memory and odd behavior

Moderators: TomKerekes, dynomotion

Post Reply
durundal
Posts: 2
Joined: Thu Jan 06, 2022 11:34 pm

DROlabel memory and odd behavior

Post by durundal » Fri Jan 07, 2022 12:13 am

Hi Tom,

I recently tried to add some extra functionality to KmotionCNC, mostly around reporting some additional DROlabels on the screen to report what is happening in the KFLOP, and implementing a toolsetter program that is activated by setting a bit through a user button. Mostly it works fine, but once out of every 10 tries or so it will not report the text in a MsgBoxNoWait as it should, instead producing either an empty OK box or an OK box with about 20 garbage characters in it (empty one attached). The Z axis stays sitting on the toolsetter in this case, and pressing OK does not cause it to move upward like the following "MoveRel(ZAXIS,18514.67366); // move up from knife to 4.0 inches off table (underside of toolsetter)" command should do, and the subsequent dialog boxes are not presented.

Another behavior is that periodically KmotionCNC will also hang, unresponsive to the mouse/keyboard, but still updating the DRO fields from the KFLOP as normal (including a coolant flowmeter that varies in use, giving a source of live data), until I unplug and replug the USB cable, at which time it recovers and works like normal. Not sure if that is related? This one happens infrequently enough that I haven't been able to deduce a common event for it happening.

I have been defining the variables and memory like below, my understanding is that the VAR field is the link from KFLOP to KmotionCNC's buttons and the MEM is the address in the KFLOP memory to be sent to that button (correct?):

Code: Select all

#define FLOWVAR 		90				// global persistant variable to store flowmeter rate
#define FLOWMEM 		1000			//used to allocate memory to be passed to DROLabels (this number is the address in 32 bit space). Size is 1000000 8 byte doubles
//#define MAX_GATHER_DATA 1000000 // Size of gather buffer (number of doubles, 8 bytes each).                   
//extern double *gather_buffer;   // Large buffer for data gathering, Bode plots, or User use
#define SPINDLETEMPVAR 	91
#define SPINDLETEMPMEM 	2000 //count of how many 32 bit words
#define XAXISTEMPVAR 	92
#define XAXISTEMPMEM 	3000
#define XAXISSLAVETEMPVAR 93
#define XAXISSLAVETEMPMEM 4000
#define YAXISTEMPVAR 	94
#define YAXISTEMPMEM 	5000
#define ZAXISTEMPVAR 	95
#define ZAXISTEMPMEM 	6000
#define COOLANTTEMPVAR 	96
#define COOLANTTEMPMEM 	7000
#define CABINETTEMPVAR 	97
#define CABINETTEMPMEM 	8000
#define INSTANTFEEDVELVAR 162
#define INSTANTFEEDVELMEM 9000
#define INSTANTFEEDVELBAR 163
#define SPINDLESPEEDVAR 164
#define SPINDLESPEEDMEM 10000

#define STATEVAR 		98				// global persistant variable to store latest state (-1=CCW,0=off,1=CW)
#define SPEEDVAR 		99				// global persistant variable to store latest speed
//variables from 100 to 107 are special (see dynomotion/Help/KMotionCNC/KMotionCNCCmdsFrKFLOP.htm )
#define TIMELEFTVAR		87
#define TIMELEFTMEM		11000
#define SMALLMSGVAR		88
#define SMALLMSGMEM		12000
#define MESSAGEVAR		89
#define MESSAGEMEM		13000

#define DRO_X1VEL_VAR	86
#define DRO_X1VEL_MEM	14000
#define DRO_X2VEL_VAR	85
#define DRO_X2VEL_MEM	15000
#define DRO_YVEL_VAR	84
#define DRO_YVEL_MEM	16000
#define DRO_ZVEL_VAR	83
And the toolsetter program is this:

Code: Select all

#include "KMotionDef.h"
#include "init_definitions.h"
//Set up to watch for toolsetter knife bit
//When knife seen, set tool Z position = height of toolsetter (nominally 



//This program always gets started by software button in KmotionCNC
void ServiceToolsetter() 
{
//measures 79.0mm when 1037 goes from high to low (using crummy calipers)
	char text[1000];
	int offer_reset = 0;

	if (ReadBit(TOOLSETTER_ACTIVE)) { // don't trigger if not in toolsetting mode

		//toolsetter bits active low
		if(ReadBit(TOOLSET_OVERTRAVEL) && ReadBit(TOOLSET_KNIFE) && !ReadBit(FEEDHOLDBIT)){
			//wait
			if(ReadBit(GREEN_SW_BY_CABINET) || !ReadBit(GREEN_SW_FAR_SIDE)){ //far side reversed function
				SetBit(1298);
				if(!ReadBit(1299)){
					Jog(ZAXIS,-500); // move down same speed as homing
					SetBit(1299);
				}
			} else {
				if(ReadBit(1298)){
					ClearBit(1298);
					ClearBit(1299);
					Jog(ZAXIS,0.0); //StopMotion
				}
			}
			
		} else {
			ClearBit(1299);
			Jog(ZAXIS,0.0); // StopMotion
			printf("exiting toolsetter\n");

			if(!ReadBit(TOOLSET_OVERTRAVEL)) {
				SetBit(FEEDHOLDBIT); //also set in read buttons
				printf("Toolsetter overtravel, feedhold set\n");
				sprintf(text, "Toolsetter overtravel, feedhold set. Position %f\n", chan[ZAXIS].Position);
				DROLabel(MESSAGEMEM,MESSAGEVAR,text); //KFLOP memory location, KMotionCNC variable (?), value
				offer_reset = 0;
				
			} else if(ReadBit(FEEDHOLDBIT)){
				printf("Aborting toolsetter sequence due to external feedhold\n");
				sprintf(text, "Aborting toolsetter sequence due to external feedhold. Position %f\n", chan[ZAXIS].Position);
				DROLabel(MESSAGEMEM,MESSAGEVAR,text); //KFLOP memory location, KMotionCNC variable (?), value
				offer_reset = 0;
				
			} else if(!ReadBit(TOOLSET_KNIFE)) {
				sprintf(text, "Toolsetting Complete. Position %f\nSet to 4.000 inches on DRO.\n", chan[ZAXIS].Position);
				// 20807.68 counts/inches (79mm)
				// trigger is 3.1102" above table 0" above table
				MsgBoxNoWait(text,MB_OK);
				MoveRel(ZAXIS,18514.67366);  // move up from knife to 4.0 inches off table (underside of toolsetter)
				//79mm = 3.110236"
				//4" - 3.110236" = 0.889763"
				//0.889763"*20807.68counts/in = 18513.93
				MsgBoxNoWait("Move to 4.000 complete\n",MB_OK);

				//ToolTableSet_am(); // adjust tool table
				printf("Toolsetting Complete\nAdjust Z to 4.000 in");
				DROLabel(MESSAGEMEM,MESSAGEVAR,"Toolsetting Complete\nAdjust Z to 4.000 in\n"); //KFLOP memory location, KMotionCNC variable (?), value
				offer_reset = 1;
			}

			if (offer_reset == 0){
				MsgBoxNoWait(text,MB_OK);
			}
			if (offer_reset == 1){
				Answer = MsgBox("Set Z to 4.000in?\n",MB_YESNO|MB_ICONEXCLAMATION);
				
				if (Answer == IDYES) {
					printf("Answer is Yes, set Z to 4.000in\n");
					DROLabel(MESSAGEMEM,MESSAGEVAR,"Answer is Yes, set Z to 4.000in\n"); //KFLOP memory location, KMotionCNC variable (?), value
					DoPCFloat(PC_COMM_SET_Z, 4.0);
				} else {
					printf("Answer is No, do not reset\n");
					DROLabel(MESSAGEMEM,MESSAGEVAR,"Answer is No, do not reset\n"); //KFLOP memory location, KMotionCNC variable (?), value
				}
			}
			ClearBit(TOOLSETTER_ACTIVE);
		}
	}
}
The toolsetter is called in the main loop (below), and I don't have other threads running when this happens.

Code: Select all

#include "KMotionDef.h" // This is the c library that KFLOP understands
#include "init_definitions.h" // this loads up the mapping of input/output channels and bits to names
#include "function_definitions.h" // load up all the definitions

#include "..\KflopToKMotionCNCFunctions.c"


#include "check_limit_switches.c" // Performs the proximity sensor limit checking
#include "init_x_motors.c" // performs initial motor configuration
#include "init_y_motor.c" // performs initial motor configuration
#include "init_z_motor.c" // performs initial motor configuration
#include "init_spindle.c" // performs initial spindle configuration
#include "startup_configuration.c" // sets all bits to appropriate startup configuration
#include "check_temperatures.c"	// reads thermistors and acts on them
#include "read_external_buttons.c" // checks for any external buttons being pressed
#include "check_alarms.c" // checks for alarms
#include "safe_system.c" // performs actions to halt motion and ensure router is in a safe configuration
#include "enable_servos.c" // enables/disables servos halt bits and axes
#include "MPGServiceSmoothHardwareEncFilteredRev3_am.c" // MPG servicer
#include "ToolTableSet_am.c" //Tool setting after hitting toolsetter
#include "ServiceToolsetter.c"// Toolsetter servicer
#include "service_time_insensitive.c" // Flowmeter and other time-insensitive read tasks
#include "service_spindle.c" //Spindle speed and flow checks
#include "abs.c" //absolute value
#include "spindle_get_speed_from_adc.c" // turns spindle ADC counts to RPM
#include "update_velocity_DROs.c" //reads axis velocities and displays on screen
//#include "readout_status.c" // prints pertinant data to console and file on disk
//#include "pause_motion.c" // stops at a convenient point for routine tool changes, stock changes, etc, returns 1 when done
//#include "graceful_shutdown.c" // stops motion gracefully and readies system for power off, returns 1 when done
//#include "mpg_smoother_modified.c" // controls the MPG pendant

//uncomment when file exists enough to compile


// Load me into thread 7 so I can be large in kilobytes and keep running even if emergency stop called in SW
// This program runs to look for general machine state monitoring and can react to things like limit switches, external buttons, etc
// It should basically always be running to make sure it stops if a limit hits or such
// Special codes (eg, MPG control) run as separate programs called from either the CNC gui or from an external switch
// For the external switch case, it will kick off the program in another thread to run independently from this one

/* Dedicated M codes
M0 - Program Stop
M1 - Optional Program Stop
M2 - Program Stop, Rewind, Reset Interpreter Settings
M3 - Spindle On CW
M4 - Spindle On CCW
M5 - Spindle Off - Note Automatically called on Program Stop/Rewind
M6 - Tool Change (T variable is passed to the C program in the persist variable specified)
M7 - Mist On
M8 - Flood On
M9 - Mist and Flood Off
*/


/* basic structure of homing for reference:

		if (!ReadBit(8))  // check for index mark
		{
			ch0->Position=0;  // set current position to Zero
			
			break;
		}
		*/

/* To do

	Enable MPG controller to not move when job running (ie Kmotion CNC needs to set flag when jobs running)
	Build program to check for servo output saturation and feedhold if so (so if one of the X axes loses itself stop entirely, other axes nice to have?)
	Build program to home axes
	Make notifications for when running into proximity sensor limits
	Rename everything to test_control instead of TestControl
*/
float nexttime;

int servoAlarm = 0;
double T0 = 0; //used for the time-remaining computations

int stopping_state = -1;

main()
{
	printf("Starting initialization tasks...\n");
	nexttime = Time_sec()+DELTATIME; //for the time insensitive thread

// Configure all outputs appropriately
	startup_configuration();

// Load up motor configurations
	init_x_motors();
	init_y_motor();
	init_z_motor();
	init_spindle();

    enable_servos(0,1); // always disable servo inputs on startup
	printf("Completed initialization tasks!\n");
    DROLabel(MESSAGEMEM,MESSAGEVAR,"Completed initialization tasks!"); //KFLOP memory location, KMotionCNC variable (?), value

	
	for(;;)
	{
			 
// Collect inputs			
		check_limit_switches(); // polls all proximity sensors and sends feedhold if found
		read_external_buttons(); // looks for any external physical buttons being pressed
		//check_alarms(); //doesn't do anything right now but looks to see if the DMMs still blip their alarm bits as they did previously before fixing grounding and adding RC filters
		update_velocity_DROs();


//Service MPG
		//ServiceMPG();
		
// Service spindle (check that commanded speed = actual speed to stop if bogged down
		service_spindle();
		
// Check spindle flow rate and other time insensitive functions
		nexttime = service_time_insensitive(nexttime);
		
//Service toolsetter
		ServiceToolsetter();
			
// Perform actions
		if (!ReadBit(MAINPOWERSTATUSBIT)) { // if don't see main power relay ...
			if (!ReadBit(FEEDHOLDBIT)) {	// ... and if this happened when we weren't in an intentional feedhold condition...
				printf("Safe system from main power and feedhold!\n");
				safe_system();				// ... perform emergency safing!
			}
			SetBit(FEEDHOLDBIT);			// always feedhold
			enable_servos(0,0);				// ensure servos are disabled
			servoAlarm = 0;					// reset the servo alarm (servos power down with main power and reset)
		}
		
		// Start feedhold check
		if(ReadBit(FEEDHOLDBIT)) {			//if something set the feedhold bit
			//StopCoordinatedMotion();		//perform the feedhold
			
			//does hammering this every cycle do anything bad?
			
			//Tests:
			//stop coordinated motion G1 commands
			//stop G0 commands
			//stop long jog command
			
			
			
			UpdateStoppingState();		// Update Stopping Status (only required for indep stopping)
			if (stopping_state != CS0_StoppingState){
				printf("CS0_StoppingState = %d\n",CS0_StoppingState);
			}
			stopping_state = CS0_StoppingState;
			
			
//extern int CS0_StoppingState; 			// emergency stop in progress, 0 = not stopping, 1=stopping coord motion, 2=stopping indep, 3=fully stopped, 4=ind stopped
			if (CS0_StoppingState == 0) { //not stopping
				StopCoordinatedMotion();
			} else {
				// motion is not coordinated e.g. G0 command or jog
				//Jog(XAXIS,0);
				//Jog(XAXISSLAVE,0);
				//Jog(YAXIS,0);
				//Jog(ZAXIS,0);
			}
			SetBit(FEEDHOLD_LAMP_SW);		//turn on the feedhold lamp
		}
		if(!ReadBit(FEEDHOLDBIT)) {
			ClearBit(FEEDHOLD_LAMP_SW);		//turn off the feedhold lamp
		}		
		// End feedhold check
		
		
		// Look to see FETs are enabled and display "on" lamp if so
		if(!ReadBit(X1MASTER_ENABLE) || !ReadBit(X2SLAVE_ENABLE) || !ReadBit(Z_ENABLE) || ReadBit(LEADSHINE_ENABLE)){		//if servos enabled
			SetBit(SERVO_LAMP_SW);			//turn on the programmable lamp
		} else {
			ClearBit(SERVO_LAMP_SW);			//turn off the programmable lamp
		}
		// end servo enabled lamp

		//Display time remaining for G-code
		double TimeRemaining = CS0_TimeDownloaded - CS0_TimeExecuted - CS0_t;
		char timeremainingtext[80];
		
		if (TimeRemaining > 0.0) {// Print only with time remaining
			sprintf(timeremainingtext,"%12.1f / %f",Time_sec() - T0, TimeRemaining);
			//sprintf(timeremainingtext,"%12.6f\n\nTime Remaining = %12.1f\n",Time_sec() - T0, TimeRemaining);

		} else {
			T0 = Time_sec(); // Time stamp last time we weren't executing motion
		}
		DROLabel(TIMELEFTMEM,TIMELEFTVAR,timeremainingtext); //KFLOP memory location, KMotionCNC variable (?), value

		
		
// Finish perform actions

		WaitNextTimeSlice();
	}
    return 0;
}

Thanks in advance,
Andy
Attachments
kmotioncnc main screen.png
broken dialog.png
correct dialog box.png

User avatar
TomKerekes
Posts: 1771
Joined: Mon Dec 04, 2017 1:49 am

Re: DROlabel memory and odd behavior

Post by TomKerekes » Fri Jan 07, 2022 5:01 pm

Hi durundal,

The code is somewhat complicated and much of it wasn't posted, but two things come to mind:

#1 text is allocated on the Thread's stack and is uninitialized so its contents can be random each time ServiceToolsetter() is entered. Actually this is somewhat large to be placed on the stack as each Thread's stack is only 2KBytes. Adding "static" in the declaration will make it global where there is 64KBytes of thread memory and also allow it to persist from one call to the next.

char text[1000];

text is sometimes sent to a message box toward the end of the routine. It's not clear that its contents are always set before it is used on every pass through the routine when it is used.

#2 - Its not clear why you are using MsgBoxNoWait. In general the way most commands to KMotionCNC work is:

(1) - KFLOP assumes no command is currently in progress.
(2) - KFLOP sets any data and parameters needed for the command
(3) - KFLOP sets the command code
(4) - KFLOP waits for the command to be completed as either successful (0) or failure (negative value) from KMotionCNC
(5) - KFLOP reads any data that the command might have returned

MsgBoxNoWait is a special case where step 4 is skipped in order to return immediately and never wait. However it is the caller's responsibility to verify the command eventually completed before issuing any other commands. If a new command is issued before the previous command is completed then data and/or commands maybe overwritten, lost, or corrupted.

HTH
Regards,

Tom Kerekes
Dynomotion, Inc.

durundal
Posts: 2
Joined: Thu Jan 06, 2022 11:34 pm

Re: DROlabel memory and odd behavior

Post by durundal » Fri Jan 21, 2022 6:04 pm

Hi Tom,

Thanks for the information. The MsgBoxNoWait were there for debugging when I was writing the toolsetter program, but it sounds like those might have been the source of the problem so I will remove them.

As for memory, is this correct:

Global memory: 64kb total
Thread memory: 2kb per thread, 14kb total for all threads
Total 78kb

Or does the 2kb per thread come out of the 64kb total? Since in this case the char arrays are all being used to update variables on the KmotionCNC screen, and those updates could potentially be from independent threads (M codes, homing sequences, etc, mostly in the form of a single-line equivalent to the Kmotion console that lives as a DROLabel on KmotionCNC), would it make more sense to declare the space for each variable as static up ahead of main() instead of allocating them inside specific functions?

I've attached my full C custom programs for posterity, the machine is a gantry router with an individual motor driving each leg of the gantry as the X axis.

Thanks,
Andy
Attachments
CNC custom programs.zip
(55.73 KiB) Downloaded 17 times

User avatar
TomKerekes
Posts: 1771
Joined: Mon Dec 04, 2017 1:49 am

Re: DROlabel memory and odd behavior

Post by TomKerekes » Fri Jan 21, 2022 8:22 pm

Hi Andy,
Global memory: 64kb total
64KB each Thread, with Thread #7 as 5 x 64KB this is external SDRAM memory
Thread memory: 2kb per thread, 14kb total for all threads
Correct. This is separate from Global and is in DSP IRAM
Since in this case the char arrays are all being used to update variables on the KmotionCNC screen, and those updates could potentially be from independent threads (M codes, homing sequences, etc, mostly in the form of a single-line equivalent to the Kmotion console that lives as a DROLabel on KmotionCNC), would it make more sense to declare the space for each variable as static up ahead of main() instead of allocating them inside specific functions
That shouldn't be necessary. The characters are copied into the global Gather Buffer before telling KMotionCNC to read them.
Regards,

Tom Kerekes
Dynomotion, Inc.

Post Reply