Hallo Allen,
Namens mijzelf ben ik op zoek naar iemand die mij kan helpen bij het leren ontwikkelen van software in Microchip (MPLAB), uiteraard tegen vergoeding. Ik snap dat maar weinig mensen hier echt op zitten te wachten, maar wie niet waagt wie niet wint. Je hebt niet te maken met een beginner die de wet van ohm net kent, maar iemand die zijn programmeerskills naar het volgende niveau (professioneel) wilt tillen.
Ik merk steeds vaker dat mijn programmeerskills ver onder niveau zijn. Ik kan opzich aardig programmeren, begrijp de structuur en werking en heb ondertussen al redelijk wat kleine prive projecten afgerond. Daarnaast heb ik zoals je mag verwachten van een afgestuurde HBO'er ook nog wat verstand van hardware. Echter vind ik dat mijn manier van programmeren nog teveel beginnerstijl is, met veel IF's en delay's.
Ik heb inmiddels:
een MBO4 opleiding Elektrotechniek - automatisering
Een HBO opleiding Electrical Engineering - Embedded systems
afgerond.
27 jaar
Helaas heb ik (blijkbaar) hier nog niet de hoeveelheid kennis opgedaan die ik zou willen. En ik begrijp dat het een kwestie van oefenen is, maar ik kom echt geen steek verder.
Wat ik vooral wil leren:
Het programmeren met timers, interrupts en het direct schrijven naar registers en vooral die eindeloze 'if/else' nests en delays achter mij te laten. Ik heb het idee dat als ik dat onder de knie heb, er echt een wereld voor mij open gaat.
Dit is geen bericht wat ik lichthartig typ. Ik schaam me er zeker niet voor, maar voel me toch een beetje verdrietig dat ik het zelf maar niet onder de knie krijg, na jaren te proberen. Ik sta bijna op punt om de elektronica voor altijd te verlaten hierdoor, om maar even dramatisch te doen 
Chris
Op 10 oktober 2023 13:08:20 schreef BDH:
Het programmeren met timers, interrupts en het direct schrijven naar registers en vooral die eindeloze 'if/else' nests en delays achter mij te laten.
Je noemt twee categorieen:
- Timers, interrupts, registers
- Eindeloze if/else, nests, delays.
Het eerste zijn een paar specifieke dingen die je tegenkomt in embedded software (ja, ik weet dat windows ook timers en interrupts heeft)
Het tweede klinkt meer als programmeerstijl, en handigheid in het opzetten van je code. Dat is in een desktop programma gemaakt met VisualBasic net zo lastig als in een embedded C programma.
Om te oefenen is het vooral belangrijk dat je een recent platform kiest met veel toegankelijk voorbeelden. Arduino bijvoorbeeld. De Atmega328 chip die op de Uno zit kun je ook met MPLAB programmeren. Kies een voorbeeldje dat een timer gebruikt, ga er mee spelen.
Qua structuur van embedded software wil ik nog roepen dat ze bijna altijd dezelfde opzet hebben:
- initialisatie
- While(1) loop.
En in die loop staat eigenlijk altijd een rijtje if/then/else statements die de aandacht van de MCU verdelen over de verschillende taken. Die taken komen heel snel in aparte functies terecht waardoor de main loop heel saai wordt:
while (1) {
if (event_een_happened())
process_event_een();
else if (event_twee_happened())
process_event_twee();
//etc, etc, etc
else
do_low_priority_stuff();
}
Dus:
- Maak een timer die 10x per minuut afgaat
- Maak een main loop die een LED toggled als de timer door nul is gegaan
- Maak een interrupt op die timer, laat de interrupt de LED togglen (een interrupt is niets anders dan een functie die rechtstreeks door de MCU aangeroepen wordt)
- print "hello world" op de RS232 UART (direct registers gebruiken)
- Zelfde, maar nu met interrupt (die aangeroepen wordt als de UART 1 character verwerkt heeft)
Probeer deze voorbeelden te maken (ze zijn met een beetje zoeken allemaal kant en klaar te vinden, maar daar leer je niets van). Loop je vast, stel dan een concrete vraag (welke MCU, welk bord, welke toolchain, wat heb je aan code en wat werkt niet).
Succes!
zet je woonplaats even in je profiel.. dan weten we ongeveer waar je woont. DIt soort dingen kun je het best met iemand 1:1 bespreken met een goede kop koffie erbij. Als je bij mij in de buurt woont, dan ben je hartelijk welkom!
Programmeren leer je door het veel te doen of door een cursus te volgen. En op internet staan ook veel voorbeelden waar je het één en ander uit kunt halen.
hardbass
PE2BAS
Als ik je een tip mag geven, laat die registers lekker zitten. Tenzij je specifieke behoeftes hebt, zou ik gewoon lekker gaan voor een library. Zoals eerder gemeld, je kunt beter een Arduino pakken. Daar zijn al talloze voorbeelden en library's voor te vinden. Als je echt praktisch iets wilt gaan maken dan is dat veel handiger.
Daarnaast wat is makkelijker te lezen:
REGA = 0x12;
REGB = ...
...
of
float myVoltage = ReadADC(PIN_1);
Je andere vraag is waar ik de focus op zou leggen. "Eindeloze if/else, nests, delays" Dit is inherent aan het goed ontwerpen van je software. Eerst nadenken dan pas schrijven. Bijna altijd is software goed op te delen in meerdere kleinere functies die een specifieke taak hebben.
Op 10 oktober 2023 13:08:20 schreef BDH:
Hallo Allen,Daarnaast heb ik zoals je mag verwachten van een afgestuurde HBO'er ook nog wat verstand van hardware. Echter vind ik dat mijn manier van programmeren nog teveel beginnerstijl is, met veel IF's en delay's.
Wat ik vooral wil leren:
Het programmeren met timers, interrupts en het direct schrijven naar registers en vooral die eindeloze 'if/else' nests en delays achter mij te laten. Ik heb het idee dat als ik dat onder de knie heb, er echt een wereld voor mij open gaat.Chris
Het leuke is dat als je in de code die later geproduceerd wordt door de compiler (assembly dus) je die if's enzo zo vereenvoudigd en wel terug vind in de assembly lijst. Een compiler bestaat vaak uit een aantal kleinere programma delen. Dat waren vroeger letterlijk een aantal kleine losse programma's die je los na elkaar moest aanroepen. Dat was bijvoorbeeld eerst de syntax van je creatie controleren, daarna compileren, assembleren en tenslotte alles linken. Later kwam daar nog een stukje optimaliseren bij. Bij dat optimaliseren werden er bijvoorbeeld dubbele stukken code uitgehaald en vervangen door een subroutine en een jump ofzo, maar ook veelvuldig in kaar geneste lussen werden daar uitgeplozen. Onze menselijke gedachten zijn vaak zo complex dat een ingewikkelde if, then else structuur zo gemaakt is terwijl dat lang niet altijd hoeft.
Tegenwoordig gaat dat compileren nog steeds zo maar ziet men dat niet meer omdat alles met behulp van een make file min of meer geautomatiseerd is. In het geval arduino is de arduino software eigenlijk alleen maar een heel handige user interface. De eigenlijke compiler is een al heel oud programmaatje met de naam AVRDUDE dat wordt aangeroepen door de arduino user interface. Dat programmaatje is ook gewoon los te gebruiken hoor. In linux staat het gewoon in de repository's en kun je het gewoon downloaden.
Om zeer complexe if's enzo te vermijden gebruik ik vaak het idee van een stappen teller. Vroeger zouden ze dat een state machine noemen. Elke stap geeft dan een toestand van de machine aan. Pas als de de toestand van de machine verandert wordt de stap verhoogt met 1. Dus stap vier is beker vullen en stap vijf is beker vol. Het omschakelen van stap vier naar vijf kan dan gebeuren door de beker vol detectie schakelaar. Ergens anders in het programma zit dan een regel dat alleen in stap vier de beker vullen klep geopend kan zijn.
Je hebt dan maar 1 if nodig. N.l.: if stap is 4 dan vul beker is aan. De andere if is dan if beker is vol dan stap wordt 5
Op deze manier kun je groot case statement bouwen of een reeks van enkele niet geneste if's.
Het is wel zaak om delay's niet te gebruiken. Die zijn te hardware afhankelijk. Ga je naar een ander hardware platform dan klopt er vaak niets meer van. Bij de oude arcade spellen was dat vaak een probleem. Met een nieuwe computer ging je spel opeens een stuk sneller. De oplossing was toen om met de timertick die elke seconde een interrupt genereerde de delay's te kalibreren. Ook vertragen ze de afwerking van het hele programma. je kunt dit oplossen met interrupts maar als het er te veel worden lopen die weer uit de stack en geeft de prioriteit weer problemen. Beter is het dan om rondlopende tellers te gebruiken die met een timer interrupt steeds verhoogd worden. Gewoon de momentele waarde optellen bij de waarde die bereikt moet worden na de versterken tijd. En dat weer vergelijken met de de nieuwe waarde van de teller. Dan is tijd voor de cpu om de bewuste actie te gaan doen. In de wachtijd kan de cpu iets anders belangrijks doen. Beter dan in een wachtlusje te lopen rondklooien.
OPTOdesign
R&D | Design | Light | Innovation | Education
Goede vraag.
Mijn ervaring is dat je via werk in de elektronicabranche het beste kunt groeien daar waar je interesses liggen.
Door over de schouder van een collega mee te kijken in de pauze, samen met een collega een project te doen of zelf voor een uitdaging te staan.
Je kunt niet altijd van baan veranderen, maar je kan wel kijken of het bij je zou kunnen passen.
Durf je (nog) niet te solliciteren als programmeur, zoek dan een baan waarbij je functie breed is en waar je op een afdeling werkt waar collega's niet allemaal hetzelfde doen als jij; liefst allemaal anders. Dan is het makkelijker om van elkaar te leren, met elkaar mee te denken en binnen je functie te groeien naar bijvoorbeeld programmeur. Zelf heb ik zeer goede ervaringen met werken in het onderwijs: Daar is 'iets willen leren' veel natuurlijker dan binnen een bedrijf waar alles alléén maar om geld draait.
Arco
Special Member
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
hardbass
PE2BAS
Wat ik zelf voornamelijk belangrijk vind, is dat de code makkelijk leesbaar moet zijn. Als je een functie ziet moet je in een oogopslag kunnen zien wat die functie doet. Er zijn tal van stukken geschreven hoe en waarom.
Een quote: (Gerald Weinberg)
"Code is written and maintained by people. Therefore, make it as readable as possible."
Op 10 oktober 2023 14:48:46 schreef Ex-fietser:
De eigenlijke compiler is een al heel oud programmaatje met de naam AVRDUDE dat wordt aangeroepen door de arduino user interface.
Eeh, bijna.
De eigenlijke compiler (voor de meeste, zoniet alle devices waar arduino mee werkt) is gcc. En gcc is een enorme mammoet van een compiler, die ongeveer alles voor iedere processor aankan, en nog steeds regelmatig bijgewerkt wordt.
avrdude is een klein programmatje om binarys (gecompileerde en gelinkte programmas, die door de CPU uitgevoerd kunnen worden) op een AVR device te programmmeren. avrdude staat volledig los van gcc.
Op 10 oktober 2023 15:19:00 schreef Arco:
In plaats van 'if/else' is een 'select case' vaak duidelijker...
Los van dat zo'n ding in C switch heet, een switch is veel beperkter dan een if/elseif/else constructie. En als je het netjes formatteert, is een switch ook niet perse duidelijker.
Zelf heb ik in mijn mainloop vaak verschillende (mogelijke) taken, die met verschillende intervallen gedaan moeten worden (bijv: Display update elke 10ms, temperatuur regeling elke seconde, keypad check elke 50ms, logfile versturen elke 5 minuten etc).
Dat kan niet netjes met een switch, wel met een if/elseif boom.
Als je daarintegen 20 verschillende commando's moet decoderen kan een switch geschikter zijn.
hardbass
PE2BAS
Als er te veel gebeurt in switches vind ik ze ook niet meer duidelijk. Een switch is wel sneller dan if / elseif statements. Tenminste, wanneer je meerdere cases hebt. Al is snelheid iets waar je tegenwoordig niet meer zo tegenaan loopt.
Voor de OOP mannen onder ons: https://refactoring.guru/design-patterns/state
Voor TS, ik denk wat je vraagt ook gewoon een kwestie van ervaring is. Ik programmeer nu zoon 6 jaar voor een baas. Daarvoor deed ik veel voor de hobby. Het ontwerpen van code is iets wat komt met de jaren. Waar ik zelf veel van leer is sparren met collega's. Het samen ontwerpen van de code voordat je gaat programmeren kan erg leerzaam zijn.
Neem de aanwijzingen van @blurp ter harte, zijn oefeningen sta ik vierkant achter, daar ga je veel van leren. Lees de (relevante delen van de) datasheet van je microcontroller. Stel je vragen hier op CO. 
Ik programmeer al van jongs af aan, zo'n 40 jaar, nooit zwaar professioneel (wel scripts,php,bash etc) verder allerhande talen en platformen.
Wat ik in sommige gevallen wel elegant vind als alternatief voor ifs of switches zijn arrays:
// duisternissensor, bewegingssensor, kind_in_bedsensor = lamp_sterkte
a[ 0 ][ 0 ][ 0 ] = 0 ;
a[ 0 ][ 0 ][ 1 ] = 0 ; //kind ligt misschien ziek op bed
a[ 0 ][ 1 ][ 0 ] = 20 ; //klein beetje licht overdag bij beweging, als test.
a[ 0 ][ 1 ][ 1 ] = 40 ; //kind springt op bed, lichtje iets feller
a[ 1 ][ 0 ][ 0 ] = 40 ; // hou boeven buiten lampje
a[ 1 ][ 0 ][ 1 ] = 10 ; // zwak lampje als kind slaapt
a[ 1 ][ 1 ][ 0 ] = 100 ; // iemand in de kamer = vol licht.
a[ 1 ][ 1 ][ 1 ] = 10 ; // zwak lampje als kind slaapt
while (true) {
setLampBrightness( a[readDarkness()][readMovement()][readBed()] )
}
Ik heb de logica niet helemaal uitgeplozen en de formatering kan netter, maar feitelijk maak je zo een 'state table' zonder enkele if/else/or/and achtige constructie.
hardbass
PE2BAS
@TS, als aanvulling op blurp zijn oefeningen:
Zonder interrupts, enkel vanuit de main:
- Maak 5 LEDS die allemaal op een andere snelheid knipperen.
- LED 1 = 1 Hz, LED 2 = 2 Hz, enz.
- Extra punten: Als een ledje 10x aan en uit is geweest mag hij de 5 seconden constant aan en daarna weer terug naar knipperen.
@K7Jz, Dat is best een interessante opzet. Als je het wat uitbreid kan je het volgende maken. Dat geeft de mogelijkheid om complexere zaken af te handelen. Nu wordt de logica echt gescheiden van de state bepaling. Je zou dat nog makkelijk kunnen aanpassen natuurlijk.
(Kladblok code, geen idee of het compileerd.)
typedef void (*StateFunction)(int stateChanged);
void emptyRoom(int stateChanged) {
if (stateChanged) {
printf("Setting lamp brightness to 0.\n");
}
}
void someoneInRoom(int stateChanged) {
if (stateChanged) {
printf("Setting lamp brightness to 100.\n");
}
}
int main()
{
a[ 0 ][ 0 ][ 0 ] = emptyRoom;
a[ 0 ][ 0 ][ 1 ] = someoneInRoom;
StateFunction currentFunctionPointer = NULL;
while (1) {
int darkness = readDarkness();
int movement = readMovement();
int bed = readBed();
StateFunction nextFunctionPointer = a[darkness][movement][bed];
int stateChanged = (currentFunctionPointer != nextFunctionPointer);
if (stateChanged) {
currentFunctionPointer = nextFunctionPointer;
}
currentFunctionPointer(stateChanged);
}
return 0;
}
@hardbass, @K7Jz:
Waar jullie langzaam heen bewegen is de formele definitie van een state-machine:
New_state = transition_matrix * (old_state + inputs)
output = output_matrix * state
Voor een embedded systeem kan dit handig zijn: Als je 200 mogelijke toestanden hebt met 16 inputs en 8 outputs ben je met 48 byte aan matrix-code, en een paar regels met 8-bit AND/OR logica in een loopje klaar.
Een switch met 200 cases, en if-elsifs voor alle 16 inputs in elke 200 cases is veel ingewikkelder.
Documenteren van deze enorme state-machine, en alle overgangen is echter een heel ander verhaal.
Voor TS is dit natuurlijk embedded programmeren 2.0, de vervolgcursus.
hardbass
PE2BAS
Klopt. Je zou ook nog zo iets kunnen doen:
EDIT: hmm, ik laat het nog even staan want ik heb het al gemaakt. Maar misschien is het beter om TS niet te veel te overdonderen met allerlei ingewikkelde code.
@TS, laat je niet afschrikken. Code leren doe je niet in een weekje.
typedef struct _args {
void (*nextState)(_args*);
bool onEntry;
} Args;
void FirstState(Args *args);
void SecondState(Args *args);
void FirstState(Args *args) {
printf("FirstState\n");
args->nextState = SecondState;
}
void SecondState(Args *args) {
printf("SecondState\n");
args->nextState = FirstState;
}
int main() {
Args args = { .nextState = NULL, .onEntry = true };
void (*currentState)(Args*) = FirstState;
for(int i=0; i<10; i++)
{
currentState(&args);
bool stateChanged = args.nextState != NULL && currentState != args.nextState;
if(stateChanged)
{
currentState = args.nextState;
}
args.onEntry = stateChanged;
}
return 0;
}
@hieronder, ha betrapt 
Dit is ook wel een beetje een eigenschap van C. Wanneer je met pointers gaat werken, zeker met functie pointers wordt het al snel een brij van haakjes. Een typedef voor de functiepointer zou al wat kunnen helpen.
Ook nog een leuke trouwens:
https://gist.github.com/FedericoPonzi/f026ff5c210e738ee03d271c8fc0f29b
Roches
Domoticz en ESP8266, goede combo!!!
Op 11 oktober 2023 09:52:16 schreef hardbass:
Klopt. Je zou ook nog zo iets kunnen doen:ONLEESBARE BRIJ
Ik las ergens dat iemand zei dat je human readable code zou moeten schrijven...
Dit valt daar in ieder geval niet onder...
Arco
Special Member
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
Leesbare (geen WORN
) code is inderdaad belangrijk, zeker voor toekomstig onderhoud ervan.
Bij een vorig bedrijf deed ik firmware ontwikkeling samen met een collega.
Een andere groep programmeurs (o.a. de baas) maakte de PC software ervoor.
Op een gegeven moment zei de baas bij een project "ik ga eens zelf de firmware maken hiervoor, lijkt me leuk als afwisseling"
Hij heeft die firmware (50+ A4'tjes) toen in 2 weken in elkaar gedraaid, en het deed bij benadering wat het zou moeten doen.
Er bleven echter heel veel vreemde dingen gebeuren. Na een weekje foutzoeken gaf 'ie het op en gaf de boel aan ons. ("kunnen jullie eens kijken wat er mis is?")
We hebben toen 2 dagen in die code gekeken, en daarna wisten we nog steeds niet wat er waar gebeurde in die code en waarom dat (wel of niet) gebeurde...
(Was een onleesbare brei van code, de baas kon zelf ook niet meer goed omschrijven hoe het eigenlijk werkte)
We hebben toe de hele boel weggekiept en zijn opnieuw begonnen... 
(en we hebben de baas gevraagd om dat nooit meer te doen en zich tot 'gewone' software te beperken, daar was 'ie uitstekend in)
Arco
Special Member
Arco - "Simplicity is a prerequisite for reliability" - hard-, firm-, en software ontwikkeling: www.arcovox.com
Op 11 oktober 2023 10:16:09 schreef Roches:
Ik las ergens dat iemand zei dat je human readable code zou moeten schrijven...Dit valt daar in ieder geval niet onder...
Er is wel wat op aan te merken, maar hardbass zijn code was niet onleesbaar.
Natuurlijk is het onzinnig om een state machine met twee states op die manier te implementeren, maar het was als voorbeeld bedoelt.
Je kunt ook discussieren over nut&noodzaak van functies voor state-overgangen, maar daar is in het algemeen wel wat voor te zeggen (Embedded systemen moeten vaak "dingen" doen bij een state overgang. Dus heb je bij die state-overgangen sowieso een functie nodig. Dus is het simpeler altijd een functie te doen)
Functie pointers in C zijn krachtig, maar ook moeilijke gereedschap. Niet voor de beginner, maar zeker ook niet uitsluiten!
MGP
LDmicro user.
Op 11 oktober 2023 11:54:12 schreef blurp:
[...]Er is wel wat op aan te merken, maar hardbass zijn code was niet onleesbaar.
...
Misschien voor een ervaren programmeur niet onleesbaar maar voor velen is het dat wel (behalve de plaatjes
).
De doelstellingen van de TS zijn ook nogal vaag omschreven en omdat hij (voorlopig) niet meer reageert is het ook moeilijk om dit topic een andere richting uit te sturen.
Gezien zijn geschiedenis denk ik dat hij meer hardware georiënteerd wil programmeren, maar daar kan de TS uitsluitsel over geven.
hardbass
PE2BAS
Dat was inderdaad ook het idee. Als je straks 10 state's hebt waar van alles en nog wat in moet gebeuren dan is het vaak leesbaarder dan een hele grote switch case. Het opdelen van de states naar hun eigen functie kan dan een middel zijn. Dat Args ding zou ik dan eerder context noemen, zodat je die kan gebruiken om bepaalde variabelen tussen states te kunnen delen.
Dit is gelijk het probleem met de definitie 'leesbare code'. Dit is nogal afhankelijk van de lezer en persoonlijke voorkeuren.
Wat ik persoonlijk nog mooier vind, is een class te maken per state zoals in mijn eerdere link stond. Maar dan moet je wel een OOP taal gebruiken.
@hardbass:
Ook zonder OOP taal en functiepointers kun je leesbare code schrijven:
enum houdingen {slaap, gaap, eet, douche, loop, zit, lig, dans, spring, speel, zing};
struct toestand {
houdingen houding;
// andere toestandsvariabelen
};
while (1) {
switch (toestand.houding) {
case slaap:
behandel_slaap(&toestand);
break;
case gaap:
behandel_gaap(&toestand);
break;
//etc
}
Dit geeft exact dezelfde machinecode als het OOP pattern met meerdere classes, maar je moet het wat meer uitspellen.
De switch wordt misschien heel lang, maar heel voorspelbaar en iedere boerel*l snapt het. Saai, maar simpel en prima te debuggen en onderhouden.
Met een tabel met functiepointers per toestand kun je de switch vervangen door een tabel-lookup. Concept komt uit FORTRAN-66, heette daar een "computed GOTO".
Afhankelijk van je source-opbouw kan de compiler dat overigens sowieso doen.
(als de compiler de enum en alle functies en de switch op hetzelfde punt beschikbaar heeft kan ie het patroon herkennen, en het implementeren met een tabel en lookup. Voordeel is dat de compiler niet in de war raakt van de vele haakjes rond een functiepointer)
