met 1 byte, heb ik nog niet geprobeerd.
de boudrate kan denk ik wel omhoog, maar mischien kan ik eerst beter eens met een logic analyzer naar de timing kijken, en vooral hoelang het sturen v/d 3 bytes nou eigenlijk duurt.

een hardware PWM of TIMER moet ik eens kijken hoe dat gaat, ik heb daar nog niet mee gewerkt.

[Bericht gewijzigd door trix op (18%)]

Op maandag 17 juni 2024 16:34:13 schreef Stijnos:
Dit is inderdaad geen interrupt, maar gewoon een functie_1 die tot 100 telt en dan een andere functie_2 uitvoert.

2x een nummer toegevoegd aan de quote

als die functie_2 klaar is voor dat functie_1 bij het volgende 100-tal is, lijkt me dat te werken.

?
een processor werkt maar 1 regel code per keer af

het opdelen van functies en interrupts betekent niet dat dingen ineens parallel gaan lopen.
alleen dat de processor hier stopt, ergens anders verder gaat, en daarna weer terug springt

een functie doet niet ineens automatisch hyperthreading ofzo
zonder een uitgebreide instructieset draait alles op 1 core
en dat niveau lijkt me nog tamelijk ver gegrepen

Je kunt natuurlijk niet gaan zitten wachten tot de eerste 2 bytes verstuurd zijn, op 9600 baud duurt dat ongeveer een milliseconde per byte. Je kunt wel de data klaarzetten en versturen zodra het transmit buffer leeg is, of zelfs elke 3de cycle van de loop een byte versturen, dan weet je zeker dat het TX buffer beschikbaar is.

als die functie_2 klaar is voor dat functie_1 bij het volgende 100-tal is, lijkt me dat te werken.

Zo werkt dat niet. Als functie 2 wordt uitgevoerd stopt ALLES van functie 1 zolang functie 2 loopt, dus ook de softwarematige teller.
Daarom wil je voor dit soort dingen hardware timers / counters gebruiken.

De 2040 is een dual core uC, dus wellicht kun je er dingen parallel mee doen, al zou dat in deze toepassing niet nodig moeten zijn.

Op maandag 17 juni 2024 19:35:51 schreef Sine:
Zo werkt dat niet. Als functie 2 wordt uitgevoerd stopt ALLES van functie 1 zolang functie 2 loopt, dus ook de softwarematige teller.

natuurlijk |:(

Zie het zo.
Je hebt nu de slede die je via een stepper vanuit software aanstuurt en die timing is voor jouw toepassing heel kritisch. Dan moet je eigenlijk de rest van de software er omheen zo maken dat die deze timing niet in de weg zit. JE zul dus heel precies moeten zijn hoe je een en ander aanstuurt.

Ga je nu buiten deze kritische aansturing van de slede ook nog dingen in een interrupt regelen dan is je aansturing naar de slede om zeep.

Maar beter is het als je deze slede aansturing buiten de software om kan maken. En dan kan je wel met interrupts gaan werken. De slede aansturing geeft je dan voor elke 100 pulsen via bv een externe deler schakeling een interrupt aan de cpu.

Dat is wel een beetje tegenstrijdig want je gebruikt nu juist een microcontroller omdat je dan zo min mogelijk hardware er omheen hoeft te bouwen maar dat is niet altijd mogelijk.

with our unique Programmable I/O (PIO) subsystem,

Zelf dacht ik dat de RP2020 een stukje eigen io hardware heeft waarin je deze kritisch aansturing dus naar de hardware kan verplaatsen. hierboven genoemd als PIO subsystem.

een hardware PWM

Als dit niet lukt in de io hardware van de rp2020 kan je ook kijken naar pwm. PWM gebeurt eigenlijk volledig in de hardware. JE stel ergens de klokfrequentie en de dutycycle in. De dc is de tijd dat een puls hoog en daarna weer laag is en dat kan van 0 tot 100% zijn. van de dc kan je denk ook het aantal bits instellen. Een 8 bit dc kan de dc in stapje van 1/256 instellen. Voor jouw is die dc niet belangrijk dat moet gewoon 50% zijn, een kloksignaal waarmee je weer een stepper driver kan aansturen.

ik heb dit ooit werkend gehad op een atmega 2560, maar ben nu omgeschakeld naar een raspberry pico.
dus nu zit ik de code (in C) terug te kijken van die atmega 2560, en lijkt het erop dat ik toen een hardware timer gebruikt heb, wellicht per ongeluk ;). maar ik weet dat niet 100 % zeker. ik had dat toen wel snel werkend.
ik heb er zelfs nog een filmpje van.

edit: ik zag in het filmpje wel dat de totale beweging over 2,5 meter 30 sec duurde (ik moet de lengte van die beweging nog eens goed meten)

edit: ik probeer die code nu te doorgronden. ik zal hem hier plaatsen, maar dan moet ik wel even kijken welk stuk ik moet hebben.


int scan_counter		= 0;

int scan_interval					= 100; // used in: scanning_movement.h
//int start_interval					= 0;   // used in: scanning_movement.h
int start_scan						= 0;   // used in: scanning_movement.h
int breake_once						= 0;   // used in: scanning_movement.h

UCSR0A |= (1 << MPCM0); // enable: multi processor communication mode
UCSR0A |= (1 << TXC0); // transmit complete
UCSR0A |= (1 << RXC0); // receive complete

UCSR0B |= (1 << RXEN0) | (1 << TXEN0) | (1 << UCSZ02);   // Turn on the transmission and reception circuit
UCSR0B |= (1 << TXCIE0); // interrupt enable when transmit is complete
UCSR0B |= (1 << RXCIE0); // interrupt enable when receive is complete

UCSR0C |= (1 << UCSZ00) | (1 << UCSZ01); // 9-bit mode, URSEL bit not needed by atmega128


UBRR0H = (BAUD_PRESCALE >> 8); // Load upper 8-bits of the baud rate value into the high byte of the UBRR register
UBRR0L = BAUD_PRESCALE; // Load lower 8-bits of the baud rate value into the low byte of the UBRR register

breake_once = 1;

while (use_scanning_movement == 1)
{
		void StartCycle( unsigned long NrSteps )
		{
			if( StepCount )
			return; // if still running ignore command
			
			StepCount = NrSteps; // record number of steps to be done
			RampCounter = 1; // start at 1; the interrupt fires when this one is done
			TCNT4 = 0; // clear counter
			OCR4A = StartInterval; // setup compare value for first cycle
			TCCR4B |= 0x04; // start timer, prescaler 256
		}
					enable_stepper_driver_LX_low; // LXU/LXT start
					dir_stepper_driver_LX_low;
					
					if( !StepCount ) // if done stepping
					{
						enable_stepper_driver_LX_high; // LXU/LXT stop						
						
						TCCR4A = 0x23; // OC1B set on compare match, clear at BOTTOM, fast PWM mode, reset on CMPA match
						TCCR4B = 0x18; // other half of fast PWM mode setting, clock stopped
						TIMSK4 = 0x02; // interrupt on CMPA match
																		
//						_delay_ms(100); // wait for a bit (usefull for logic analyser)
						StartCycle( 26000 ); // length of the movement
					}					
					
					if ( 26000 - StepCount > scan_interval) // make a start trigger 
					{                                       // to scan every 10 mm (100 pulses)
						scan_interval = scan_interval + 100;						
						start_scan = 1;											
					}
					
					if ((LS_LXUF_reached) && (breake_once == 1)) // if limit scwitch is reached
					{
						breake_once = 0;
						StepCount = 1000;
					}
					
					if ((LS_LXTF_reached) && (breake_once == 1)) // if limit scwitch is reached
					{
						breake_once = 0;
						StepCount = 1000;
					}
					
					if (StepCount < 10) // jump out of the loop on the end of the movement
					{
						use_scanning_movement = 0; // jump out of the loop
					}					
					
//					if (LS_LXUF_reached) { StepCount = 1000; }
//					if (LS_LXTF_reached) { StepCount = 1000; }					
					
				    //below: send comando to TSAL & TSOP: do your scan					
					if (start_scan == 1)
					{
						start_scan = 0;
						
						PORTH |= (1 << PINH7); // LED = 1 just for checking ROOD ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
						
						PORTE |= (1 << PINE2); // switch MAX485 as transmitter pin 2 & 3 = "1"
						PORTE |= (1 << PINE3); // switch MAX485 as transmitter pin 2 & 3 = "1"	
						
						UCSR0B |= (1 << TXB80); //Set the TXB8 bit (9e bit) to 1: we gonna send a addres
									
						for (scan_counter = 0; scan_counter < 5; scan_counter ++)
						{		
						// below: TSAL	
							//*** adress numbering: TSAL 1-20 and TSOP 21-40 **********************************************
							_delay_ms(1);
							while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
							bytetosend = scan_counter + 1; // 1 is the adress TSAL				
							UDR0 = bytetosend;
					
							_delay_ms(1);
							while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
							bytetosend = 0b00001111; // is the data: start your scan
							UDR0 = bytetosend;
					
							_delay_ms(1);
							while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
							bytetosend = 0b11111111; // is the data witch is the stop code
							UDR0 = bytetosend;
				
							// below: TSOP			
								//*** adress numbering: TSAL 1-20 and TSOP 21-40 **********************************************
							_delay_ms(1);
							while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
							bytetosend = scan_counter + 21; // 1 is the adress TSOP				
							UDR0 = bytetosend;
														
							_delay_ms(1);
							while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
							bytetosend = 0b11110000; // is the data start your scan
							UDR0 = bytetosend;
					
							_delay_ms(1);
							while ((UCSR0A & (1 << UDRE0)) == 0) {}; // do nothing till the UDR is ready to receive
							bytetosend = 0b11111111; // is the data witch is the stop code
							UDR0 = bytetosend;
							
							PORTH &= ~(1 << PINH7); // ^^^^^^^^^^^^^^^^^^
							}
						}
								
} // from: while (use_scanning_movement == 1)

			if( StepCount )
			return; // if still running ignore command
			
			StepCount = NrSteps; // record number of steps to be done
			RampCounter = 1; // start at 1; the interrupt fires when this one is done
			TCNT4 = 0; // clear counter
			OCR4A = StartInterval; // setup compare value for first cycle
			TCCR4B |= 0x04; // start timer, prescaler 256

Hier heb je timer counter 4 gebruikt met

TCNT4
OCR4A
TCCR4B

De io mogelijkheden van RP2020 zijn nog een stuk groter omdat die zijn io subsystem heeft. IK denk dat de hele aansturing nu in de hardware zou moeten kunnen, maar dan moet je je wel gaan verdiepen in hoe dat werkt.

En met hele aansturing bedoel ik dan dat je elke 100 pulsen een interrupt krijgt waarop je in de software kan reageren.

die timer/counter 4 is dus een hardware timer ?

Misschien eens kijken naar de broncode van GRBL, een gcode 'interpreter' om xyz tafels aan te sturen.

De stappenmotoren draaien op hoge snelheid, ondertussen worden bytes die binnenkomen via de serial interface gedecodeerd en worden er ook nog bytes teruggestuurd naar de verzender van de gcode (o.a. bCNC)

Dit loopt allemaal "very smooth".

't zal wat lezen worden en uitzoeken, maar misschien wel de moeite waard. Wat de ontwerpers van deze tool hebben klaargespeeld in een ATMega328 is echt wel knap.

En draait ondertussen ook op een RPI2040.

Groetjes,
eSe

zal ongetwijfeld wel werken, maar waarschijnlijk veels te veel overkil voor die simpele dingen die ik er mee doe.

Op maandag 17 juni 2024 20:53:44 schreef trix:
die timer/counter 4 is dus een hardware timer ?

ja

Eigenlijk is het gewoon een counter maar omdat die counter direct geklokt kan worden via de oscillator en die pulsen een vaste tijd hebben is het ook een timer

TCNT4 is de counter zelf

Die andere weet ik niet precies mar dat kan je opzoeken ik dacht dat
TCCR4B de timer capture en compare register is. De counter TCNT4 word dan steeds vergeleken met dit register en als ze dan gelijk zijn kan je daar een interrupt mee triggeren maar je kan het ook gebruiken om PWM in te stellen.

gebruik je externe stepper drivers die je enkel een pulsje moet geven die dan zelf de half of full stepping doet of doe je dat zelf in je micro?
Ik vermoed het eerste.

Dan moet je dus een timer interrupt hebben die elke ms een pulsje kan maken als je 1000 pulsen per sec wilt.
Je wilt dan eens in de 100 van deze interrupts 3 bytes versturen.
Dat moet makkelijk kunnen. Je interrupt van 1 ms treed elke 1 ms op, maar duurt hopelijk maar een fractie daarvan.
de transmissie tijd van 1 byte op 9600 baus is volgens mij 10 bits (start + stopbit meegerekend) / 9600 = nagenoeg ook 1ms.
Maar daar gaat het volgens mij niet om.

Als jij op tijdstip X je eerste byte in het uart register stopt, vanuit die stepper interrupt bijvoorbeeld, dan treden er +- 1ms later 2 interrupts op.
1 de volgende stepper pulse en de tx complete van de uart.
Het is zaak dat je in die tx complete interrupt niet meer doet dan de volgende byte in de uart schrijven en klaar. vervolgens doe je je pulsje maken en gaat alles verder.
redelijk basic allemaal en zolang je in je interrupt functies niet meer doet dan strikt noodzakelijk gaat dit goed. (ik vermoed dat je interrupts niet de efficientste zijn)
Als je de baudrate verhoogd naar zeg 115K2, dan zijn die 3 bytes al lang en breed de deur uit voor jouw volgende 1ms pulse komt.

Als je nou echt geen tussen liggende interrupts per byte zou willen hebben, kun je nog DMA gebruiken, maar om dat uit te leggen zullen we je maar besparen gezien de al 10 jaar durende voorgeschiedenis van dit project ;)

Het is dus helemaal niet zo tijd kritisch allemaal als je denkt en het is al helemaal niet nodig om hardware matig de 100 pulsen te tellen en dan pas een hw interrupt te genereren.
Het is juist heel gebruikelijk dat je vele interrupts gelijktijdig krijgt, het is enkel zaak ze efficient en zo snel mogelijk af te handelen. Met een micro die op vele Mhz draait vandaag de dag is dat geen enkel probleem.

Nu denk ik dat je in micro python niet zo in details gaat programmeren, maar copilot suggereerd een volgende oplossing (let op hij is niet 100% compleet, maar geeft grofweg een voorbeeld wat je in de goede richting zou kunnen brengen.


import machine
import time

# Initialize the GPIO pin for the pulse output
pulse_pin = machine.Pin(14, machine.Pin.OUT)

# Initialize the UART (adjust the parameters as needed)
uart = machine.UART(1, baudrate=9600, tx=17, rx=16)

# Initialize a pulse counter
pulse_count = 0

# Define the interrupt handler
def pulse_interrupt_handler(timer):
    global pulse_count
    pulse_count += 1
    if pulse_count == 100:
        # Send a 3-byte message over UART
        uart.write(b"ABC")
        pulse_count = 0

# Set up a timer to generate the 1 ms pulse
timer = machine.Timer(0)
timer.init(period=1000, mode=machine.Timer.PERIODIC, callback=pulse_interrupt_handler)

# Main loop (you can add other functionality here)
while True:
    # Do other tasks if needed
    time.sleep_ms(10)  # Sleep to avoid busy-waiting

met 9600 baud zit je hier mogelijk wat lastig, maar ik weet niet hoe hij onder water de uart transmissie doet.
Probeer het eens en ander verhoog je simpelweg je baudrate gewoon naar 115K2 ofzo

Op maandag 17 juni 2024 23:14:43 schreef Stijnos:
gebruik je externe stepper drivers die je enkel een pulsje moet geven die dan zelf de half of full stepping doet of doe je dat zelf in je micro?
Ik vermoed het eerste.

eerste inderdaad. steppendriver met pulse, direction en enable

ik lees het straks nog een keer aandachtig door, ben nu aan het werk.

ik zat net te bedenken dat de raspberry pico dual-core heeft, eens kijken of ik daar wat mee kan.

Ja da heeft deze wel maar dan nog is het een goed idee om als het kan zaken in de hardware af te handelen. In ieder geval kan de 1kHz puls door de hardware gemaakt worden. zoals je dat eerder ook in de mega gedaan hebt.

Je denkt allemaal veel te moeilijk. Dual core helemaal nergens voor nodig. Wat je wilt kun je zelfs gewoon op een 8 bit atmega nog prima voor elkaar krijgen. Het gaat hier niet om hele tijd kritische dingen. Ik heb wel spannendere dingen gezien en gemaakt met veel minder resources.
Je moet die pulse voor je stappen motor controller wel gewoon met een interrupt maken, anders heb je zowieso geen gecontrolleerde beweging.

Ik zat eens mee te lezen en vroeg mij af welke 3 bytes er moeten verzonden worden, is dat de positie van de wagen na elke 100pulsen?

het worden straks meer bytes, die 3 was een 1e test.
1e = adres byte
2e = do your scan byte
3e = stop byte

dit zou dus voor ieder adres moeten, nu voor de eerste testen heb ik 10 adressen nodig, dit kan later nog 24 adressen worden.
wat ik werkend had was 10 adressen.

die 3 adressen duren ong. 3,5 mSec.

ik heb met opzet gekozen als 1e voorbeeld voor 3 adressen, omdat (in ghet verleden) het heel lastig is gebleken uit te leggen hoe het een en ander werkt.

Oww er wordt dus in de toekomst een communicatieprotocol opgezet, zenden en ontvangen.
Ik dacht laat de motorsturing over aan een aparte controller en laat de rest over aan de RPI maar als het zo zit dan heb je nog een hele weg te gaan :S

Op donderdag 20 juni 2024 11:20:46 schreef MGP:
Ik dacht laat de motorsturing over aan een aparte controller en laat de rest over aan de RPI

wat bedoel je met een aparte controller ?
edit: voor de duidelijkheid ik gebruik geen raspberry pi maar een raspberry pi pico (dat is al een micro controller)

hoe maak je nu je pulsen voor die stappen motor controller dan?
Begin die eens vanuit een interrupt te maken.

die worden gemaakt in een for loop, incl. acceleration & decceleration & software limitswiches & homing.
maar dat werk allemaal al naar behoren, niks mis mee :+

[Bericht gewijzigd door trix op (11%)]