klopt dekees, duidelijk, ook mijn dank.

Op 28 december 2019 20:50:04 schreef trix:
is mij al vaker verteld dat croppen, ik begrijp nu pas wat er bedoeld word.
pak ik eerst even aan.
even kijken hoe dat gaat in paint ?

Openen in paint, dan het "Select" blokje aanklikken en dat stuk selecteren wat je precies wilt zien -> Crop knopje ernaast klikken en klaar is trix.

kijk, ook het croppen is gelukt.......op naar het volgend probleem.

en dat is niet een probleem, maar een vraag:
als ik nu nog een blok toevoeg met de naam "controls" moet ik dan weer een apparte .h file toevoegen. of mag ik daarvoor de bestaande homing.h voor gebruiken ?

[Bericht gewijzigd door trix op (61%)]

Wat je maar wilt, alles kan. De compiler vindt het allemaal goed. Dus je kunt de nieuwe procedure toevoegen aan main.c, of aan honing.c (en dan ook aan homing.h) en je kunt ook een NieuweFunktie.c en NieuweFunktie.h aanmaken.

De meeste mensen vinden het handig als funkties die bij elkaar horen in dezelfde source-file staan. Dan kun je ze later weer makkelijk terugvinden. En dan kun je die module ook later makkelijker in andere projecten her-gebruiken.

nou was ik vandaag een hd44780 display aan het testen (ook voor dit project) en daar maak ik gebruik van een libary afkomstig van electrosome.com het viel mij op dat die een .h extensie heeft, en dat de code gewoon in deze .h file staat geschreven.

vraag: waarom kan ik hier niet de code in de .h file schrijven ?

Dat kan wel, maar hoort niet...

Kan wel. En wordt ook steeds meer gedaan.
De beroemde 'boost' library bestaat enkel uit header files. Net als een groot deel van de standard template libraries.

Maar dan moet je wel goed weten wat je doet.
Als je een header file in meerdere source files include binnen je project, dan krijg je multiple definities van je funkties en dan loop je vast tijdens het linken. Dus dan kun je beter de declaraties (.h files) apart houden van de implementatie (.c files)

Op 28 december 2019 23:43:53 schreef deKees:
De beroemde 'boost' library bestaat enkel uit header files. Net als een groot deel van de standard template libraries.

Zeg ook maar gerust "berucht", die zitten niet bepaald simpel in elkaar. En dat zijn grotendeels alleen de interfaces en bak macros en templates, dat zijn uiteraard alleen header files.

Verder zitten er natuurlijk de libraries bij die apart gecompileerd moeten worden (naar *.so of *.dll files). Dus toch C-files: alleen gebruik je die niet (direct) in je eigen project. Dus die stelling is niet helemaal waar.

Net als een groot deel van de standard template libraries.

Die horen in een header file, dat kan niet anders.

Op 28 december 2019 21:43:58 schreef trix:
...en dat de code gewoon in deze .h file staat geschreven.

vraag: waarom kan ik hier niet de code in de .h file schrijven ?

Code in een .h kan, en er is een goede reden om dat te doen:

Om de compiler de kans te geven die code te inlinen. Korte functies zijn soms groter in functie-call overhead dan in daadwerkelijke functionaliteit.
Toch wil je ze in een functie, want dat houd de kode overzichtelijk en duidelijke.
Bijvoorbeeld een SetGreenLed(bool value) functie. bestaat waarschijnlijk (op AVR) maar uit een instructie, maar het is veel duidelijker als de details van op welke pin de led zit niet zichtbaar zijn in main.

Door die functie als inline in de header te definieren, kan de compiler de hele functie-overhead weglaten en alleen de led aanzetten, terwijl jouw kode leesbaar blijft.

wat ik een aantal posts terug zei over de termologie die voor julie gesneden koek is en voor mij moeilijk. komt nu tot uiting in de post van blurp.
moeilijk te begrijpen voor mij door de moeilijke termen. begrijp mij niet verkeerd blurp je inbreng word door mij enorm gewardeerd.

ik denk dat ik dan toch zoals dekees en henri62 zeiden een apparte .c en .h ga gebruiken.
hoe overzichtelijker hoe beter voor mij.

ik heb vandaag niet de tijd gehad om het een en ander te testen. maar nu ik een beetje zit door te denken, zie ik toch een aantal onduidelijkheden.

1- hoe kan ik bepalen wanneer er naar homing.c word gesprongen ? is dat de volgorde van de includes in de main.c die dat bepaald ? (dat werd ergens in dit topic aan gehaald) en kan ik ook bepalen vanaf welke plek er naar homing.c word gegaan ?
of moet ik me voorstellen dat de programma pointer continu door de main.c gaat en ook continu door homing.c. en als dat klopt, hoe kom ik dan in homing.c terecht ?

2- als ik in main.c een variabele heb gemaakt, kan ik die dan ook in homing.c gebruiken en visa verca ?

main.c en homing.c zijn source files. Die worden door atmel studio als input gebruikt om een .hex file aan te maken, die dan in de flash van de target processor wordt geladen. De target avr weet niks van soutce files maar voert de instrukties uit de .hex file uit.

De compiler heeft die zo opgebouwd dat de program counter begint in de main() funktie en dan stap voorstap het programma volgt. Als je de homing() funktie tegenkomt, dan wordt die uitgevoerd. Voor de processor maaakt het geen verschil uit welke sourcefiles die funkties komen.

Je kunt in je programma wel variabelen uit andere sourcefiles gebruiken, maar dan moet je diewel declareren, dwz de compiler moet wel weten dat die bestaan.

Als in main.c bijv een variabele 'int x;' hebt, en en je wilt die in homing.c gebruiken, dan moet je in homing.c de statement 'extern int x;' toevoegen. Meestal doe je dat dan via een main.h header file.

Op 29 december 2019 17:06:13 schreef deKees:
Als je de homing() funktie tegenkomt, dan wordt die uitgevoerd. Voor de processor maaakt het geen verschil uit welke sourcefiles die funkties komen.

die homing() functie staat in de homing.c file, dan is hij dus al van de main.c naar de homing.c gegaan.
wanneer en hoe ?


#include "homing.h"

void main()
{
   while( true)
   {
      homing();
   }
}

die moet dan in de main.c denk ik.

Ja, dat kan, maar hoeft niet.
De naam van de sourcefile kun je zelf kiezen. Als je maar ervoor zorgt dat de file gekoppeld is aan je project in atmel studio.

Je moet die functie aanroepen waar je die nodig hebt.

Verder een functie waar niks in gaat en niks uit komt, zoals in dit voorbeeld is raar. Wat wil je in die homing() functie gaan stoppen, post dat eens hier?

Even terugkomen op die header files. Een goede library definieerd sommige functies inderdaad als "inline" in een header file. Maar wordt alleen gedaan als ze relatief kort zijn.
Kort is hier een beetje een ruimer begrip. Vaak zijn het functies die uiteindelijk compileren tot maar een paar machine instructies.

Je moet als schrijver van zo'n library weten wanneer het efficienter is om code te "inlinen" dan deze in de C-source te zetten.
Er zijn voor inlining nog wat andere zaken van belang zoals het gebruik van variablen in die "inlined" functie. Voor een beginner: gebruik het niet. Het levert relatief weinig op. En als je het echt nodig hebt ben je waarschijnlijk al dusdanig ervaren dat je het tzt al weet waarom wel en niet.

hieronder het stukje code "homing"
dit werkt, het moet alleen nog worden uigebreid met meerdere assen.
kan wellicht efficienter, maar dat moet ik mischien nog uitzoeken......eerst laten werken.
nu wil ik dat ergens op een door mij bepaald punt in main.c de hieronder staande code word uitgevoerd, en daarna weer keurig verder gaat in de main.c waar die gebleven was.

//***********************************************************************************************************
//*** homing RX *********************************************************************************************
//***********************************************************************************************************

			if homing_button // if you push the home button
			{				
				if (automatic == 1) // switch manual/auto stands on auto
				{
					speed_select = 1; // in homing mode the speed select is always 1					
										
					enable_stepper_driver_RX_low; // DM556 enable = 0
					dir_stepper_driver_RX_high; // DM556 DIR = 1 go to the left									

//*** acceleration *****************************************************************************************			
						x = desired_speed_RX [speed_select] - 1000; // acc. distance = about 100 mm
						for (pulse = 0; pulse < 1000; pulse ++) // number of pulses i need
						{			
							x = x + 1;
				
							pulse_stepper_driver_RX_high; // pulse to stepper driver = 1
							_delay_us(5);
							pulse_stepper_driver_RX_low;    // pulse to stepper driver = 0
				
							TCNT1 = x;
							TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
				
							while ((TIFR & (1<<TOV1))==0);
							TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
				
							TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"
						}
			
//*** moving during homing, unless the limit switches are reach ************************
						for (pulse = 0; pulse < 32000; pulse ++) // 32000 because its more then 3000mm what is the length of the rail
						{						
							
//*** if on of the limit switches are reached ******************************************************************

							if (LS_RXUB_reached || LS_RXTB_reached ) // if limit switch = reached
							{
								pulse = 32000;
							}					
						
							pulse_stepper_driver_RX_high; // pulse to stepper driver = 1
							_delay_us(5);
							pulse_stepper_driver_RX_low;    // pulse to stepper driver = 0
				
							TCNT1 = x;
							TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
				
							while ((TIFR & (1<<TOV1))==0);
							TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
				
							TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"					
						}
			
			
						for (pulse = 0; pulse < 1000; pulse ++) // dec. distance = about 1000 mm
						{
							x = x - 1;
				
							pulse_stepper_driver_RX_high; // pulse to stepper driver = 1
							_delay_us(5);
							pulse_stepper_driver_RX_low;    // pulse to stepper driver = 0
				
							TCNT1 = x;
							TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
				
							while ((TIFR & (1<<TOV1))==0);
							TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
				
							TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"
						}
			
							_delay_ms(500); // wait moment before you can start a new movement
							enable_stepper_driver_RX_high; // DM556 = 1 = disable
//*********************************************************************************************************
//*** homing LX	*******************************************************************************************
//*********************************************************************************************************
					
					enable_stepper_driver_LX_low; // DM556 enable = 0
					dir_stepper_driver_LX_high; // DM556 DIR = 1 go to the left									
			
						x = desired_speed_RX [speed_select] - 1000; // acc. distance = about 100 mm
						for (pulse = 0; pulse < 1000; pulse ++) // number of pulses i need
						{			
							x = x + 1;
				
							pulse_stepper_driver_LX_high; // pulse to stepper driver = 1
							_delay_us(5);
							pulse_stepper_driver_LX_low;    // pulse to stepper driver = 0
				
							TCNT1 = x;
							TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
				
							while ((TIFR & (1<<TOV1))==0);
							TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
				
							TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"
						}
			
//*** moving during homing, unless the limit switches are reach ************************
						for (pulse = 0; pulse < 32000; pulse ++) // 32000 because its more then 3000mm what is the length of the rail
						{						
							
//*** if on of the limit switches are reached ******************************************************************
							if (LS_LXUB_reached || LS_LXTB_reached ) // if limit switch = reached
							{
								pulse = 32000;
							}					
						
							pulse_stepper_driver_LX_high; // pulse to stepper driver = 1
							_delay_us(5);
							pulse_stepper_driver_LX_low;    // pulse to stepper driver = 0
				
							TCNT1 = x;
							TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
			
							while ((TIFR & (1<<TOV1))==0);
							TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
			
							TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"					
						}
			
			
						for (pulse = 0; pulse < 1000; pulse ++) // dec. distance = about 1000 mm
						{
							x = x - 1;
				
							pulse_stepper_driver_LX_high; // pulse to stepper driver = 1
							_delay_us(5);
							pulse_stepper_driver_LX_low;    // pulse to stepper driver = 0
				
							TCNT1 = x;
							TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
				
							while ((TIFR & (1<<TOV1))==0);
							TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
				
							TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"
						}
			
							_delay_ms(500); // wait moment before you can start a new movement
							enable_stepper_driver_LX_high; // DM556 = 1 = disable
							
							X_position = 0; // now were homed left & right so X_position= 0
											 
				} // from: if automatic = 1							
			} // from: if homing_button

Dit is een blok code. Die kun je alleen van buitenaf aanroepen als je daar eerst een funktie van maakt.

Dus een funktie header bovenaan en een sluithaakje onderaan:

In homing.c :


void homing()
{
  // Dan hier het blok met de code
}

Dan kun je die vanuit main() aanroepen met
In main.c :


void main()
{
   // ... Andere code
   homing();
   // ... Andere code
}   

Maar zo simpel is het niet omdat in het blok code een groot aantal variabelen gebruikt wordt die in beide files op een of andere manier gedefinieerd moeten worden. Dus dat vraagt nog wat extra werk.

En daar zijn verschillende methodes voor. Dan moet je voor elke variabele bepalen of die alleen in homing.c of ook in main.c bekend moet zijn. En vervolgens moet je definieren hoe je de data tussen de twee modules wilt uitwisselen.

Het makkelijkste is om alle variabelen in homing.c te zetten en dan de nodige 'extern' declaraties in de homing.h te zetten zodat alles ook vanuit main.c beschikbaar is.

Meestal is het ook wel handig om een deel van de variablen als funktie parameter te definieren. Dan kun je die gemakkelijk doorgeven vanuit de funktie aanroep.

Maar een betere methode is om die variabelen in een struct of een class te definieren. Maar dat vergt wat meer werk.

ik begin een klein beetje de indruk te krijgen dat deze methode om lange programma's overzichtelijk te maken wellicht niet de juiste methode is.
word dit vaker zo gedaan ?

Dat is de standaard methode. Die wordt overal toegepast.

[edit]
Feitelijk is dit de enige mehode methode om stukken code over meerdere sourcefiles te verdelen. Het kan wel anders maar dat is zo vies dat ik dat niet ga uitleggen.

Het begin is even lastig omdat het huidige funktie-blok zo niet is opgezet. Maar als je daar eenmaal doorheen bent dan heb je er alleen nog maar plezier van.

[Bericht gewijzigd door deKees op (29%)]

Als ik de code zo zie is het dus inderdaad de "homing" van iets van een stappenmotor.

Het zou logisch zijn om alles wat met die stappenmotor te maken heeft dus in 1 file te stoppen. Die zou je dus geen "homing.c" moeten noemen maar bijvoorbeeld: steppercontrol.c

Als je dan alle variablen en functies die daarmee te maken hebben (en nergens anders gebruikt worden) ook in die file stopt wordt het allemaal een stuk overzichtelijker.
Bijvoorbeeld de variable: X_position, desired_speed_RX[] etc.
Dus gewoon effe compileren en output kijken waar die over zeurt en dat oplossen.

Alle variablen die je alleen in 1 functie gebruikt maak je "local", een van die variablen is 'x'.
Verder kun je "pulse" ook locaal definieren (kan ook in de for() zelf afhankelijk of de compiler C99 mode ondersteund).

Ik neem aan dat 'x' een 8-bit waarde is als ik zo de code zie en dat je ook #include <stdint.h> boven aan in je code hebt staan.

Zo niet moet je "uint8_t" vervangen door "unsigned char".

void homing()
{
  uint8_t x;
  int pulse;

  // Dan hier het blok met de code
}

Nu zie ik ook dat de functie homing zelf al veel te lang is om die snel te overzien, dat vraagt dus om wat structuur verbetering.

Ik zie dat er bijvoorbeeld een for-next loop in zit met elke keer hetvolgende stukje code:

	TCNT1 = x;
	TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
	
	while ((TIFR & (1<<TOV1))==0);
	TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
	TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"

Dat kun je weer in een functie stoppen (in dezelfde file) die genoemd is na wat je probeert te doen, in dit geval denk ik:

waitForTimer(uint8_t delay) {
	TCNT1 = delay;
	TCCR1B |= (1<<CS10);  // prescaler = 1 = start timer
	
	while ((TIFR & (1<<TOV1))==0);
	TCCR1B &= ~(1<<CS10);  //prescaler = 1 = stop timer
	TIFR|= (1 << TOV1); // make overflow flag "0" yes you have to write a "1"
}

In je blokken vervang je nu elke plaats waar dat stukje code staat door:

waitForTimer(x);

Zo kun je stap voor stap de code opschonen zodat uiteindelijk elke functie nog maar een beperkt aantal regels heeft, een vuistregel is dat je de hele functie op je scherm kunt zien.
Dit proces heet "refactoren" van code. Dus is heel normaal en iedereen doet dit.
Eerst begin je moet een grove structuur van je programma en al doende komen de stukjes (functioneel) bij elkaar en schoon je de boel al naar gelang de tijd op.

Zo kun je dit stukje ook weer vervangen door een functie:


pulse_stepper_driver_LX_high; // pulse to stepper driver = 1
_delay_us(5);
pulse_stepper_driver_LX_low; // pulse to stepper driver = 0

Een ervaren programmeur refactored een stuk minder omdat die, als het goed is, eerder/vanaf het begin van de code al een betere stuctuur opgezet heeft en grotendeels code al in de juiste files plaatst, maar ook die ontkomt er niet aan.

code is inderdaad voor het homen van een stepper.

het is denk ik inderdaad beter om alles van een stepper motor in 1 file te stoppen, het is denk ik de bedoeling om variabele I/O pinnen en functies zo min mogelijk over de verschillende files te verdelen. en dan is dat zeker beter.

x heb ik als een long. maar die telt er iedere keer 1 bij op in een for loop die 1000 x word uitgevoerd. dan kan dat toch nooit in een 8-bit waarde ? x kan later maximaal 50000 worden hier heb ik dus een 16-bits nodig. ik weet even niet meer waarom ik toen voor een long heb gekozen, dat is toch 8 bytes ?
include <stdint.h> heb ik niet in mijn code staan.
x is trouwens slecht gekozen, het kan makelijk verward worden met iets v/d X-as. veranderen dus.

ik wist wel dat je vaak repeterende stukje code maar 1x hoef te schrijven, dat stukje dan een naam geven, wat je vervolgens kan gebruiken. maar wist toen even niet hoe dat te doen, en het doel was een werkende code te schrijven, dus daar maar niet teveel tijd in gestoken. maar nu is wel het moment aan gebroken om dat te gaan doen. er komt nog veel code bij.

ik ben er wel achter gekomen dat wanneer je gewoon voor de voet weg programeerd je snel in een onoverzichtelijke situatie beland. en ik wist uit dat beetje ervaring wat ik heb dat dat ging gebeuren, dus had er al zoveel mogelijk structuur in mijn code aan gebracht, en rijkelijk voorzien van comentaar. maar dat is niet voldoende in een code die in 1 file zit.
opsplitsen dus in min of meer losse stukjes en deze linken.

Ah, TCNT1 is dus een 16 bit counter. Die heb ik als referentie genomen. Dus zou je die variable als 'uint16_t' moeten definieren.

Een 'long' is trouwens meestal 32-bits, dus niet geschikt in dit geval.

Over het algemeen is de meest efficiente variable een 'int' in zo ongeveer elke type processor.

Ik gebruikt altijd vaste type breedte variablen als ik weet dat ze in registers van een CPU geschreven worden. Met natuurlijk het juiste type. Dus een 8-bit register => uint8_t

Voor gewone simpele variablen tellertjes en loop counters is een 'int' vrijwel altijd een betere keuze. Een variable uint8_t maken als die van 0-10 loopt levert vrijwel geen code reductie op, sterker nog soms wordt de code (netto assembly dus) zelfs groter.

Ik ben in ieder geval blij dat je nu zelf ook ziet dat die x ongelukkig gekozen is.

Op 30 december 2019 21:03:52 schreef henri62:
Over het algemeen is de meest efficiente variable een 'int' in zo ongeveer elke type processor.

int is inderdaad gespec'd als de meest efficiente variable voor de target processor, MAAR: minimaal 16 bit.

Dus op een AVR (en daar hebben we het hier over) is een int 16 bit, maar de meest efficiente variabele 8 bit. En dus niet int.

Dan kun je nog bekvechten of je uint8_t (en int8_t) gebruikt of unsigned char (en signed char); maar dat is simpel: Als de variabele een getal representeerd is het (u)int8_t, alleen als het een teken is is het een char.
Dan staat er tenminste wat je bedoelt, en dat maakt je kode beter (ook al maakt de compiler er onder water een char van).