functie in een functie

trix

Golden Member

hallo,
ik heb een stukje repeterende code die ik in een functie wil "gieten"
echter bezit die code al een functie (SPI_transfer), dus krijg je denk ik een functie in een functie. of er komt een geheel nieuwe opzet.
code is er voor om een byte uit een ext.RAM te halen m.b.v. SPI.
ik post een stukje code waar alles in staat.
hoe kan ik dit in 1 functie krijgen ?
alvast bedankt.

c code:


uint8_t SPI_transfer(uint8_t data) // functie
{
	SPDR = data;
	while(!(SPSR & (1<<SPIF))); // Wait for transmission complete
	return SPDR;
}

uint32_t address;

PORTA |= (1 << PINA0); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

for (Y_scan = 0; Y_scan < 37000; Y_scan = Y_scan + 1000)
{
	for (X_scan = 0; X_scan < 300; X_scan ++)
	{
	//	RAM_address = Y_scan + X_scan + 1;
		RAM_address = X_scan + 1;
		
		PORTG &= ~(1 << PING5); // make SS ext. RAM pin = 0
		SPI_transfer(0b00000011);  // read data from memory
		address = RAM_address; // is address in ext. RAM 
		
		SPI_transfer((address>>16)); // address
		SPI_transfer((address>>8)); // address
		SPI_transfer((address)); // address
		
		SPI_transfer(0x00); // dummy when you want to read a byte from ext. RAM

		PORTG |= (1 << PING5); // make SS ext. RAM pin = 1
		
		check_byte_1 = SPDR; // 76543210		
	
eigenwijs = ook wijs

Functie in een functie is geen probleem zolang er genoeg stack is...

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

En dat "genoeg stack" moet je je gewoon geen zorgen over maken. Maar ik heb het in je andere topic al voorgedaan. Dus waarom WEER een nieuw topic over dat ene project?

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
trix

Golden Member

omdat het ver weg raakte van de eigenlijk vraag.
en omdat ik dat stukje van jou niet begreep, maar ik ga het nog eens goed bekijken.

[Bericht gewijzigd door trix op 30 mei 2020 20:06:38 (47%)]

eigenwijs = ook wijs

Meeste compilers hebben iets van een 'statistics' functie om de stackdiepte te bekijken.
Bij normaal gebruik zul je er inderdaad zelden last van hebben, bij recursieve aanroepen wel...

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com
trix

Golden Member

ik ben aan het kijken hoe ik bij mij een functie kan toe passen. ik neem daarbij de comentaren van rew als leidraad (sommige staan in een ander topic).

hij zij;

code:

    PORTG &= ~(1 << PING5); // make SS ext. RAM pin = 0
    SPI_transfer(0b00000011);  // read data from memory
    address = RAM_address + 1; // is address in ext. RAM from the byte right to the checked byte

    SPI_transfer((address>>16)); // address
    SPI_transfer((address>>8)); // address
    SPI_transfer((address)); // address

    SPI_transfer(0x00); // dummy when you want to read a byte from ext. RAM

    PORTG |= (1 << PING5); // make SS ext. RAM pin = 1

    check_byte_2 = SPDR; // 76543210

is volgens mij het lezen van een byte uit een externe RAM.

Maak daar een functie van:

code:


    check_byte_2 = ram_read (RAM_address+1);

Dat maakt de code overzichtelijker omdat je in dit deel van de code niet bezig bent met "SPI hardware aansturen" maar gewoon met bytes uit ram halen.

dus dat probeer ik dan toe te passen, met de volgende code als resultaat:

c code:


int ram_read (int data)
{
	PORTG &= ~(1 << PING5); // make SS ext. RAM pin = 0
	SPI_transfer(0b00000011);  // read data from memory
	address = data; // is address in ext. RAM from the byte right to the checked byte

	SPI_transfer((address>>16)); // address
	SPI_transfer((address>>8)); // address
	SPI_transfer((address)); // address

	SPI_transfer(0x00); // dummy when you want to read a byte from ext. RAM

	PORTG |= (1 << PING5); // make SS ext. RAM pin = 1

	return SPDR; // 76543210
}

check_byte_2 = ram_read (RAM_address+1);

klopt dit ?
ik zie even niet hoe check_byte_2 de waarde van SPDR krijgt (waarschijnlijk met de return) de schrijwijze: check_byte_2 = ram_read (RAM_address+1); is denk ik wat verkort.

en waar moet ik nu de functie: SPI_transfer neer zetten ?
die is als volgt:

c code:


uint8_t SPI_transfer(uint8_t data) // functie
{
	SPDR = data;
	while(!(SPSR & (1<<SPIF))); // Wait for transmission complete
	return SPDR;
}

ik heb ook ergens gelezen dat de functies altijd voor de main moeten staan. bij mij is dat minstens in 1 situatie niet het geval,...en toch werkt het.

eigenwijs = ook wijs

In klassiek "C" hoef je functies niet te declareren. Maarrr... dan declareert de compiler hem voor je met "int" als return type. Mocht dat tzt anders zijn, dan kan het zijn dat ie verkeerde code genereert.

code:


// hier is de declaratie, vooraankonding van een functie. 
// float test2(void); 
// Nu dus als commentaar omdat ie voor deze demo NIET aangekondigd moet zijn. 
void test1 (int t)
{
  printf ("%d\n", t+test2());
}

float test2 (void)
{
  return 4.0;
}

zou niet moeten werken. Of op z'n minst, hoeft niet te werken. Als de compiler er een zooitje van maakt houdt ie zich aan de standaard.

Tegenwoordig vind men het wel fijn om functies gedeclareerd te hebben. Dat is niet 100% verplicht, maar gewoon een goede programmeer-gewoonte.

Dat kan je op diverse manieren doen. Ofwel je declareert bovenaan je sourcefile (of in een .h file) al je functies. Ofwel je definieert je functies voordat je ze gebruikt. Locale functies probeer ik dat laatste te doen.

Omdat "main" nooit door een andere functie aangeroepen wordt, kan je daarom main het beste als laatste zetten. En functie die je achter main zet moet je dan altijd vooraf declareren, het: ik zet hem wel VOOR de plek dat ie gebruikt wordt werkt dan niet. (bij tenminste 1 functie die AcHTER main staat).

Maar als jij om je programma uit te leggen (aan anderen die het proberen te begrijpen, of jezelf over 2 jaar) het handiger vind om met "main" te beginnen dan kan dat. En dan is het verstandig om alle functies van te voren even te declareren.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
trix

Golden Member

ik heb de code getest zoals ik had gepost, en het werkt.
structuur is nu als volgt:
vind dit wel duidelijk, neerzetten op de plek waar je hem gebruikt.

gelijk een vraag:
is het mogelijk om vanuit een functie 4 waardes te "returnen" ?

main()
.
.
.
if (joy_stick_right) // tijdelijk zo om te testen
{
#include "scan_the_RAM.h"
}

en dan in een appart tabblad (scan_the_RAM):
.
.
.
uint8_t SPI_transfer(uint8_t data) // functie
{
.....
}

int ram_read (int data) // functie
{
....
}
.
.
.
rest v/d code

eigenwijs = ook wijs

is het mogelijk om vanuit een functie 4 waardes te "returnen" ?

Ja, pointer naar een type/structure retourneren. (of byref doorgeven)

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Voor de compiler is een include gewoon een stuk source-code wat op die plek ingevoerd wordt.

Maar ook hier geldt: eigenlijk is het niet voor code bedoeld maar voor declaraties en zo.

code:


/* ram.h */

#define RAM_READ 0b00000011

int ram_read (int addr); // functie

extern int page;

In ram.c heb je dan:

code:


#include "ram.h"
/* ram.c */
int page; 

int ram_read (int addr)
{
  int v;

  ACTIVATE_RAM_CS ();
  spi_transfer (RAM_READ);
  spi_transfer (page);
  spi_transfer (addr >> 8);
  spi_transfer (addr >> 0);
  v = spi_transfer (0);
  DEACTIVATE_RAM_CS ();
  return v; 
}

en in main.c alleen:

code:


#include "ram.h"

ergens aan het begin.

Merk op dat ik een paar dingen "gefixed" heb. Je "data" heb ik nu addr genoemd. Omdat dit 16 bits is en je weigert m'n declaratie van 32 bits over te nemen is het kennelijk de bedoeling dat je RAM nooit meer dan 64k heeft. Om de bits 17-24 uit een 16-bit variabele te halen is ook een beetje onzin. dus dan maar zo.

Jou aanroepende code deed nog steeds iets met het SPDR. Dat moet je niet doen. Als je de code netjes geschreven hebt, dan zou je zomaar over kunnen schakelen op een i2c RAM chipje zonder je hoofd-code aan te passen.

Of misschien heb je later een CPU waar WEL genoeg geheugen in zit.

code:


unsigned char buffer[RAM_SIZE];
int ram_read (int addr)
{
  return buffer [addr]);
}

De ram-read functie doet het goed: adres in, data teruggeven. Nu nog de aanroep fixen.

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
trix

Golden Member

nog eens een paar keer over lezen, is niet zo simpel :S

eigenwijs = ook wijs

@rew:

c code:


int ram_read (int addr)
{
  return buffer [addr]);
}

Dat sluithaakje hoort er niet! Enne: laat de spatie tussen buffer en het [ haakje weg (en ook tussen de functie definitieen het eerste open haakje), leest anders voor geen meter.
Zo dus:

c code:


int ram_read(int addr)
{
  return buffer[addr];
}
Henri's Law 1: De wet van behoud van ellende. Law 2: Ellende komt nooit alleen.
trix

Golden Member

Op 1 juni 2020 11:36:46 schreef Arco:
[...]
Ja, pointer naar een type/structure retourneren. (of byref doorgeven)

dit is een antwoord op de vraag of het mogelijk is om vanuit een functie 4x een waarde te "returnen"

ik heb gekeken naar een structure, maar ik zie niet hoe ik dat zou kunnen toepassen.
zou iemand dat kunnen toelichten ?

eigenwijs = ook wijs

nou vooruit, nog maar eens een tipje :)

c code:


typedef struct {
	uint16_t a;
	uint16_t b;
	uint16_t c;
	uint16_t d;
}T_4abcd;

T_4abcd FunctionReturn4Things(void) {
	T_4abcd retVal;
	retVal.a = 10;
	retVal.b = 20;
	retVal.c = retVal.a + retVal.b;
	retVal.d = retVal.a * retVal.b;
	return retVal;
}

void FunctionReturn4ThingByReference(T_4abcd* ref) {
	ref->a = 10;
	ref->b = 20;
	ref->c = ref->a + ref->b;
	ref->d = ref->a * ref->b;
}

void main(void) {
	T_4abcd var;
	var = FunctionReturn4Things();
	//but I pref not returning structs, if needed return by reference
	FunctionReturn4ThingByReference(&var);
}

ps. hoever sta je met dit project? Gaat het ooit goed komen denk je? ;)

Je kan ook altijd gebruik maken van "globale" variabelen. Deze zijn ook buiten de functie bereikbaar . In principe moet je dit vermijden, omdat dit een bron is van programmeerfouten (als je 1000den regels code hebt, kan je al wel eens vergeten of een variabele lokaal of globaal is...). Maar via een "reference" of "structure"doe je eigenlijk hetzelfde : het adres van de variabele wordt bereikbaar buiten de functie ! Je geeft dus eigenlijk het adres door waar de functie een variabele heeft veranderd. Dit is onbeperkt in aantal, je kan zoveel adressen doorgeven als gewenst.

Merk nog op dat FunctionReturn4Things een structure van meer dan "een paar bytes" teruggeeft. De mechanismes om in een CPU een return waarde terug te geven zijn daar niet op berekend. Een moderne compiler zal toch de standaard moeten implementeren en moet iets verzinnen.

Het gevolg is dat er nogal wat memcpy acties moeten plaatsvinden als je het op die manier doet. Gebruik indien mogelijk die "by reference" methode. (Tenzij je gegronde reden hebt om het toch te doen).

four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/

(als je 1000den regels code hebt, kan je al wel eens vergeten of een variabele lokaal of globaal is...)

Simpel op te lossen: laat globale variablelenamen beginnen met g_... en/of locale met l_...

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

ieujw :)

Je compiler mekker daar toch gewoon over als er een dubbele variabele in een inner scope is.

Ik zou ook zeker voor de by reference gaan. Nu is het een relatief kleine structure, maar ik heb veel grotere gezien.
Het probleem is anders ook dat je de gehele structure op de stack gaat zetten, omdat je hem in je functie moet declareren om vervolgens te returnen (tenzij je dat static doet).

Op 2 juni 2020 09:02:55 schreef Stijnos:
...
Je compiler mekker daar toch gewoon over als er een dubbele variabele in een inner scope is.

Optimist, het volgende werkt prima in c++

code:

#include "pch.h"
#include <iostream>
int var = 10;

int main()
{
	int var;

	var = 20;
	printf("Var is %d\n", var);
}

Ik gebruik altijd de prefix g_ voor globale variabelen, m_ voor class members (en s_ als het een static member is).
Je kan die laatste twee ook oplossen door consequent this-> te gebruiken, maar ik vind m_ makkelijker.

De compiler vindt het trouwens ook prima als je de global als volgt declareert:

code:

float var = 10.0;

.
Oops, een float en int met dezelfde naam...
En geen melding van de compiler.

[Bericht gewijzigd door JoWi op 2 juni 2020 09:36:50 (14%)]

Ik gebruik indien mogelijk altijd de 'hungarian notation' om verwarring te voorkomen...
Zou in bovenstaand geval iVar en fVar worden...

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com

Op 1 juni 2020 23:14:18 schreef Stijnos:
nou vooruit, nog maar eens een tipje :)

c code:


typedef struct {
	uint16_t a;
	uint16_t b;
	uint16_t c;
	uint16_t d;
}T_4abcd;

T_4abcd FunctionReturn4Things(void) {
	T_4abcd retVal;
	retVal.a = 10;
	retVal.b = 20;
	retVal.c = retVal.a + retVal.b;
	retVal.d = retVal.a * retVal.b;
	return retVal;
}

Dit werkt wel maar is vaak inefficient en hangt af van de compiler hoe de data uiteindelijk gecopieerd wordt in de calling environment. Ik vermijd dit soort constructies vooral en doe een pass by reference met een pointer naar de return waarde. Zoals in het 2-de voorbeeld.

Henri's Law 1: De wet van behoud van ellende. Law 2: Ellende komt nooit alleen.

Op 2 juni 2020 09:51:21 schreef Arco:
Ik gebruik indien mogelijk altijd de 'hungarian notation' om verwarring te voorkomen...
Zou in bovenstaand geval iVar en fVar worden...

Dit is nu precies hoe de hungarian notation NIET bedoeld is en door vele programmeurs compleet verkracht is. Die variablen zeggen nog steeds niks.

Lees anders dit artikel maar eens hoe het echt bedoeld is: Making wrong code look wrong
Een van de vele goede artikelen die Joel Spolsky geschreven of verzameld heeft.

[Bericht gewijzigd door henri62 op 2 juni 2020 17:09:43 (10%)]

Henri's Law 1: De wet van behoud van ellende. Law 2: Ellende komt nooit alleen.

Als je goede namen geeft aan variabelen is de hungarian notation juist wel erg handig, je ziet meteen wat voor var en wat 'ie doet...
Nee, dan de 'normale' c programmeurs, die vaak hun variabelen de namen a...z geven. Da's handig en overzichtelijk. :(
Loopvariabelen noemen ze altijd i, j, k in C, heel handig... ;) (alleen omdat ze te beroerd zijn om een fatsoenlijke naam te geven en meer als 1 letter in te typen...)

Arco - "Simplicity is a prerequisite for reliability" - www.arcovox.com
trix

Golden Member

Op 1 juni 2020 23:14:18 schreef Stijnos:
ps. hoever sta je met dit project? Gaat het ooit goed komen denk je? ;)

tuurlijk :) gaat dit goed komen, zoals ik al vaker heb gezegd is het qua software heel goed op te splitsen in blokken die na elkaar komen. blokken zijn per stuk niet super ingewikkeld, mischien wel voor mij...maar met hulp van o.a. julie lukt het wel _/-\o_ .
hoever.......ik ben nu bezig om de data die in de externe RAM staat (afkomstig v/d veelvuldig hier besproken scanner) om te zetten naar data die de steppers gaat sturen. dus als er in de RAM een figuur staat die er uit ziet als een blokhaak, ben ik daarvan nu de hoeken aan het opsporen (6 stuks) van die 6 hoeken moet ik de X-Y coordinaten (op een matrix van 300 x 300) hebbben.
dus een buiten hoek komt dan 5x voor, dat kan dus b.v. een functie worden die 2 waardes returnt (X & Y). mijn vraag was 4 waardes, maar met 2 ben ik ook al heel blij (splits ik dan in 2 functies).
in een hoop dingen ben ik ongeduldig van nature, al word dat minder met het ouder worden (elk nadeel heb zijn voordeel). maar met dit soort dingen (eigenlijk met alle technische dingen) heb ik engelen geduld, al zit ik 3 dagen naar een code te gluuren.

ik ga de reacties nog bestuderen (moet 2 dagen weg voor het werk), maar iedereen bedankt daar voor.

eigenwijs = ook wijs

Op 2 juni 2020 18:33:29 schreef Arco:
Als je goede namen geeft aan variabelen is de hungarian notation juist wel erg handig, je ziet meteen wat voor var en wat 'ie doet...
Nee, dan de 'normale' c programmeurs, die vaak hun variabelen de namen a...z geven. Da's handig en overzichtelijk. :(
Loopvariabelen noemen ze altijd i, j, k in C, heel handig... ;) (alleen omdat ze te beroerd zijn om een fatsoenlijke naam te geven en meer als 1 letter in te typen...)

Ieder zijn stijl en mening he ;)
Ik zou zeggen als je je variabelen gewoon een goede duidelijke naam geeft heb je het probleem van dubbele in verschillende scopes niet zo snel.
Ook als.je je variabele scope zo klein mogelijk houd is het ook niet echt nodig denk ik.
Met goede IDEs van tegenwoordig vind ik type aanduiding in de naam echt niet meer nodig. Als je eroverheen hovert zie je het type.
Maar goed, zoals ik al zei. Ieder zijn stijl.