Hallo!

Dit is mijn eerste post op dit forum, ook al hang ik hier al een tijdje rond om te kijken en lezen e.d. Eerder was het voor mij nog niet nodig om me in een discussie te mengen (wegens gebrek aan kennis) of om een topic te starten (gezien ik op al mijn vragen een antwoord wist te vinden in dit forum)

Nu echter mijn probleem.

Ik ben met een projectje bezig dat een MIDI signaal via een PIC16F628a wordt ingelezen. Het programma isoleert dan het clock signaal en stuurt deze door als een puls van 20us naar een 4017 Decade Counter "sequencer". Deze "sequencer" zou uiteindelijk moeten werken met acht 1/16e noten en dit werkt probleemloos.
Maar ik wil graag de frequentie van dit signaal een aantal keer verdubbelen of halveren (door middel van een 'increase' en 'decrease' knop) en ook dit werkt, maar zeer beperkt!
Het probleem waar ik tegenaan loop is dat de MIDI Clock met 24 PPQN (24 pulsen per 1/4e noot) wordt verstuurd. Dat betekend dus dat op een standaard midi clock signaal er 6 pulsjes zijn per 1/16e noot. Door te halveren (3 pulsjes per 1/16e noot) verdubbel je de snelheid, maar het probleem is waarschijnlijk al duidelijk: dit is ook meteen de max!

Is er een mogelijkheid om een frequentie vermenigvuldiger (in de pic, of erbuiten met componenten) toe te passen zodat ik de snelheid meerdere keren kan verdubbelen tot zeg 16x de normale frequentie?

Ik zal even mijn huidige code meeplaatsen:

DEVICE 16F628A                
CONFIG HS_OSC, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              
XTAL 20                       ;20 MHz crystal
HSERIAL_BAUD  = 31250         ;Transmission speed on serial BUS (31250 BAUD = MIDI standard)
HSERIAL_RCSTA = %10010000     ;Bit7:Enable serial port / Bit4:Continueous receive
HSERIAL_TXSTA = %00000000     ;Bit5:Transmitpin disabled (TXEN (Transmit ENable) = 0)
HSERIAL_CLEAR= ON
SYMBOL Clock = PORTB.3        ;pulse output on pin B3
SYMBOL BtnDec = PORTB.6       ;button to increase speed
SYMBOL BtnInc = PORTB.7       ;button to decrease speed
SYMBOL btnON = 0                 ;inversed ON (btn)
SYMBOL btnOFF = 1                ;inversed off (btn)

DELAYMS 500                   ;stabilization

DIM ontvangen AS BYTE         ;received data from MIDI
DIM PPQN AS BYTE              ;24 Pulses Per Quarter Note (standard)
DIM speed AS BYTE             ;value = 6 for speed of BPM from MIDI

PORTB_PULLUPS ON              ;On-chip pull-up resistors active
CLEAR 

speed = 6                     ;default speed

;main program
WHILE 1 = 1                   ;Run loop
    FOR PPQN = 1 TO speed     ;
        HSERIN [ontvangen]    ;receive MIDI messages
        IF ontvangen != $F8 THEN  ;Is the received message not a MIDI Clock message?  
            PPQN = PPQN - 1   ;Then lower the counter with one
        ENDIF
    NEXT
    PULSOUT Clock , 10 , HIGH ;Send a pulse 20 us long (@20MHz)
    
    IF BtnDec = btnON THEN    ;halve the speed
        IF speed != 24 THEN   ;speed=24 (1/4x) is max
            speed = speed * 2
        ENDIF
    ENDIF 
    
    IF BtnInc = btnON THEN    ;double the speed
        IF speed != 3 THEN    ;speed = 3 (2x) is max
            speed = speed / 2
        ENDIF
    ENDIF 

WEND                          ;End of Run loop
    
END                           ;End of program

PS. Ik heb in de PIC met een interupt routine op timer0 wel al de BPM weten te berekenen, alleen werkt dat ietwat omslachtig. De huidige (overzichtelijke) manier werkt als output zeer goed

Hoe nauwkeurig moet de MIDI-input gevolgd worden?
Is het bijvoorbeeld haalbaar om het genereren van de klokpulsen helemaal los te koppelen van de MIDI-verwerking?
Oftewel: in je MIDI routine bepaal je alleen de inkomende kloksnelheid. Deze pas je vervolgens naar wens aan (delen, vermenigvuldigen, etc) en je gebruikt het resultaat in een 'stand-alone' routine om de uitgangspulsen op te wekken.

Als je zeker weet dat de verhouding "MIDI clk":"output clk" een geheel getal is, kun je de output gesynchroniseerd houden met MIDI, maar alleen op de klokpulsen die in theorie precies samenvallen.
Bijv bij een factor 3:2 kun je maximaal elke 6 pulsen synchroniseren.

Bedankt voor je reactie.

Ik weet niet zo goed of ik jou helemaal begrijp of dat je mij niet helemaal hebt begrepen (door mijn misschien gebrekkige uitleg)

Mijn intentie is om een soort van sequencer te maken die qua snelheid gelijk of geschaald (machten van twee) loopt met een ander apparaat welke een Midi Clock genereert.

In eerste instantie keek ik naar een 1:1 verhouding, en dat lukt me om te creeren (met het stukje basic bijgevoed in TS).

Zoals je zei, ik weet zeker dat de verhouding "midi clk":"output clk" een geheel getal moet zijn volgens 1:2^n of 2^m:1 (met n en m uit 0 tot zeg 4)

Echter, de moeilijkheid zit hem in het feit dat het midi clock signaal niet is aan te passen omdat deze ten eerste standaard op 24 PPQN is, en ten tweede uit een ander apparaat komt. 24 is géén macht van 2 dus lastig om mee te werken. Gezien 6 pulsen overeenkomt met een 1/16e noot, kan je dus enkel versnellen met mijn stukje PicBasic door niet te tellen naar 6, maar naar 3 (halve periode -> dubbele frequentie) en sneller kan dus niet. Daarom moet er dus iets met de uitgaande pulsen worden gedaan, bijvoorbeeld een frequentie vermenigvuldiging.

Om terug te komen op je eerste vragenblokje. De MIDI-input (de clock) wil ik zo nauwkeurig mogelijk navolgen. En of het haalbaar is de pulsgenerator los te koppelen weet ik niet, ik zou eerlijk gezegd ook niet weten hoe. Ik heb hier behoorlijk op zitten studeren, maar ik kom met mijn gebrekkige kennis niet echt verder. Wellicht moet ik weer terug naar het berekenen van de BPM uit de midi-clock en dan dit om te zetten naar een PWM met 50% duty. Dat laatste zou wel makkelijk te schalen zijn, alleen ik krijg dat in picbasic niet voor elkaar om allemaal tegelijk te doen.

In theorie zou je een heel nauwkeurige klok kunnen maken in je PIC-programma die de benodigde resolutie heeft voor alle mogelijke kloksignalen die je wilt genereren.

Vervolgens gebruik je de inkomende MIDI kloksignalen alleen om die interne klok te synchroniseren. En tussen die synchronisaties loopt de klok in de PIC uit zichzelf.

Het is dan natuurlijk de grote truc om je software zo te maken dat de klok in je software soepeltjes meeloopt met tempowisselingen... tenminste... ik neem aan dat bij een afwijkend tempo ook de MIDI klok verandert.

Dat van die clock zou moeten kunnen inderdaad. Hoe ik dit softwarematig moet aanpakken is dan een tweede... Heb zelf wel al wat programmeervereleden, echter PicBasic is voor mij vrij nieuw en laat staan het interrupt gedeelte. Ik was al blij zat dat ik door trial and error met Timer0 van de midi clock een vrij nauwkeurige benadering (error < 5%) van de BPM wist uit te spugen.

Waar ik vast liep was dat ik van de BPM, die inderdaad bepalend is voor de frequentie van de Midi Clock pulsen, geen continu lopende 50% (of lager) duty squarewave out kon krijgen. Is dit mogelijk met HPWM?

of kan dit nauwkeurig in een interrupt routine? Ik heb, tevergeefs, gezocht naar interrupt tutorials voor in picbasic. Als iemand daar enige informatie over heeft in de vorm van een link of zelfs eventueel een boek, graag!

@Franzki: dat was inderdaad wat ik bedoelde, maar dan wél begrijpelijk geformuleerd :-)

@Ronniebiggs: afhankelijk van de waarde van het klokkristal heb je met de PWM outputs inderdaad hardwareondersteuning voor het genereren van je output. Is waarschijnlijk nodig ook, mijn ervaring met PicBasic is, dat het niet snel is.

In de midi-input-routine hoef je niet alles al te kunnen berekenen aan de hand van 1 enkele klokpuls!
Je zou bijvoorbeeld eerst 24 clocks kunnen tellen, je weet dan hoelang 1 beat duurt. Of je neemt 96 clocks en je weet hoe lang 4 beats duren.

Met deze gemeten tijd stel je de PWM-registers in, en laat je hem lopen.

Stel dat je van 120 BPS naar 180 BPS wilt. Je kunt dan elke 6 beats synchroniseren. Dat is dus elke 6*24=144 clocks.
In de midi-input-interrupt kun je dan telkens tot 144 tellen. Als het zover is, reset je dat tellertje weer, en zet je het lopende PWM-register geforceerd op nul, zodat deze weer gelijkloopt.

De andere 143 tellen laat je de PWM-unit gewoon lekker zijn gang gaan en doe je niets.

Ik ben geen PIC-expert... het ging me vooral om het concept.

Je hebt een klok die in je PIC loopt. Je voorspelt wanneer er de volgende MIDI-klok binnen zal komen... komt die eerder dan verwacht, dan moet de interne klok sneller gaan lopen... komt de MIDI-klok later dan verwacht, dan moet de interne klok langzamer gaan lopen.

Het wordt dus bijna een regelsysteem. De vraag is vooral of je verwacht dat je veel tempowisselingen hebt en hoe soepel jouw klok moet reageren om snel in de pas te lopen.

Ik zal even volledig de sluier van het project afhalen om te delen wat ik uiteindelijk van plan ben, misschien toch wel handig om te melden ter verduidelijking.

Het doel is een kastje te maken met een 8-step sequencer die door middel van wat CMOS 4000 series chips een 8-staps geluid kan produceren (regelbaar per stap). Een vriend van mij is DJ en hij zou zo'n apparaatje graag in zijn sets kunnen gebruiken. Hij heeft een aantal effectenbakken naast zijn draaitafels die in staat zijn een midi-clock door te geven. Hij draait vooral minimal en techno (120-140 BPM) en daarmee moet het dus synchroon kunnen lopen. Gedurende de set zal er een subtiele wisseling zijn van het tempo, vandaar dat mijn 'creatie' zichzelf synchroon moet trekken.
Het is echter niet belangrijk dat exact stap 1 en 5 gelijk vallen met de bass-drum/beat!

Dat bovenste heb ik al werkend.

Nu wil ik het uitbreiden door de sequencer dus een aantal maal sneller of langzamer te laten lopen zoals ik eerder heb genoemd.

Qua timing zit je dus op 2 tot zeg 2,333 beats per seconde wat dan overeenkomt met 48 tot 56 pulsen per seconde.

Wat is dan een goede sampletijd om je timers in te stellen? is een factor 10 goed? of factor 100?

Ik had eerder al geprobeerd (en lukte aardig) om dus de BPM te berekenen aan de hand van een timer0 overflow interrupt door het verschil in de timer te nemen van voor en na 24 midi-pulsen (zoals Fox2 ook al voorstelde), echter ik zag hierbij dat ook al was de totale error <5%, het berekende aantal BPM was niet altijd constant en fluctueerde ook binnen diezelfde error marge.

hier een stuk werkende (BPM voorspellende) code:
VERWIJDERD

EDIT: Wegens wijziging (extreem) verwijderde code

[Bericht gewijzigd door Ronniebiggs op (48%)]

Even een update:
Ik heb inmiddels de software volledig werkend!
Moest hier en daar wat variabele verschuiven en tweaken.
Maar het meeste wat ik heb aangepast is wel dankzij jullie (Fox2 en Franzki) suggesties en wedervragen.
Het grootste probleem dat ik had (lage frequentie van de output) heb ik weten op te lossen. Het was mij ook ontschoten dat er een minimum frequentie van 1221 Hz @20MHz is voor HPWM alsook een maximum frequentie van 32767Hz. Ik heb dit opgelost door een frequentie output te kiezen die binnen deze marge valt en dan door een Ripple Counter wordt gedeeld door 1024 zodat er 1 puls per 1/16e noot overblijft. Door het schuiven van variabelen is ook mijn foutmarge geslonken tot een fractie, zie nu helemaal geen wisselingen meer in de BPM en tot 180 BPM is er geen error aanwezig.

Ik heb nu ook het op en neer schalen werkende gekregen! Mijn dank is groot heren! (naar ik aanneem, anders mijn oprechte excuses)

Wellicht dat ik nog een en ander moet toevoegen voordat het op een printje komt (LED indicators i.p.v. LCD scherm, extra bediening etc)
Maar voorlopig werkt het en zie hier het huidige resultaat:

'****************************************************************
'*  Name    : MIDI2.BAS                                          *
'*  Author  : Ron Burgstede                                     *
'*  Notice  : Copyright (c) 2010 Ron Burgstede                  *
'*          : All Rights Reserved                               *
'*  Date    : 15-7-2010                                         *
'*  Version : 1.0                                               *
'*  Notes   :                                                   *
'*          :                                                   *
'****************************************************************
DEVICE 16F628A                ;Gebruik een 16F628A type
CONFIG HS_OSC, WDT_OFF, PWRTE_ON, LVP_OFF, MCLRE_OFF
ALL_DIGITAL TRUE              ;Alle ingangen digitaal
XTAL 20                       ;Gebruik een 20 MHz kristal
ON_HARDWARE_INTERRUPT GOTO IntRoutine 
                       
HSERIAL_BAUD  = 31250         ;Transmission speed on serial BUS (31250 BAUD = MIDI standard)
HSERIAL_RCSTA = %10010000     ;Bit7:Enable serial port / Bit4:Continuous receive
HSERIAL_TXSTA = %00000000     ;Bit5:Transmitpin disabled (TXEN (Transmit ENable) = 0)
HSERIAL_CLEAR= ON

;DECLARE LCD_ENPIN PORTA.3    ;in case of LCD
;DECLARE LCD_RSPIN PORTA.2    ;in case of LCD

; Assign some Interrupt associated aliases 
 SYMBOL T0IE = INTCON.5         ' TMR0 Overflow Interrupt Enable 
 SYMBOL T0IF = INTCON.2         ' TMR0 Overflow Interrupt Flag 
 SYMBOL GIE = INTCON.7          ' Global Interrupt Enable 
 SYMBOL PS0 = OPTION_REG.0      ' Prescaler ratio bit-0 
 SYMBOL PS1 = OPTION_REG.1      ' Prescaler ratio bit-1 
 SYMBOL PS2 = OPTION_REG.2      ' Prescaler ratio bit-2  
 SYMBOL PSA = OPTION_REG.3      ' Prescaler Assignment (1=assigned to WDT 0=assigned to oscillator)  
 SYMBOL T0CS = OPTION_REG.5     ' Timer0 Clock Source Select (0=Internal clock 1=External PORTA.4)
 SYMBOL Clock = PORTB.3       ;PWM clock-out pin
 SYMBOL btnInc = PORTB.2      ;Increase speed button
 SYMBOL btnDec = PORTB.0      ;Decrease speer button
 SYMBOL pressed = 0           ;button is pressed

PORTB_PULLUPS ON              ;pullup resistors on for PORTB (for buttons)

TRISB.3 = 0                   ;PORTB.3 output of CCP module (HPWM)

DELAYMS 500                   ;stabilization
;CLS                          ;Clear Screen of LCD
DIM serReceive AS BYTE        ;holds MIDI byte
DIM sixteenthNPSx100 AS DWORD ;16th Notes Per Second X 100 (2 digits)
DIM PulseFreq AS DWORD        ;HPWM Frequency
DIM BPMx100 AS WORD           ;BPM x 100 (2 digits)
DIM PPSN AS BYTE              ;6 Pulses Per Sixteenth Note (standard = 24 per quarter note)
DIM counterint AS WORD        ;interrupt overflow counter
DIM average AS DWORD          ;holds average time
DIM sum AS WORD               ;sums up the 5 time variables
DIM stopcnt AS WORD           ;holds interrupt overflow countervalue at one 16th note received
DIM RippleScalar AS WORD
DIM tel0 AS WORD, tel1 AS WORD, tel2 AS WORD, tel3 AS WORD, tel4 AS WORD ;holds old stopcnt values for averaging
DIM Scale1 AS BYTE, Scale2 AS BYTE   ;together form the speed Scale1:Scale2 (default @ 1:1)
CLEAR                         ;Clear all variables

;  Variable Init
RippleScalar = 1024           ;4040 Ripple Counter pulsout division at port :1024
tel0 = 155                    ;to set the default average at 155
tel1 = 155                    ;     which results in a default 120 BPM
tel2 = 155
tel3 = 155
tel4 = 155

GOTO Over_interrupt           ; Jump over the interrupt subroutine 

;  Interrupt routine starts here 
IntRoutine: 

 T0IF = 0
 TMR0 = 6
 INC counterint               ; Clear the TMR0 overflow flag 
 CONTEXT RESTORE              ; Restore the registers and exit the interrupt 
 
Over_interrupt: 

;  Initiate the interrupt 
 GIE = 0                         ' Turn off global interrupts 
 PSA = 0                         ' Assign the prescaler to external oscillator 
 PS0 = 1                         
 PS1 = 1                         
 PS2 = 0                         
 T0CS = 0                        ' Assign TMR0 clock to internal source 
 TMR0 = 6                        ' Clear TMR0 initially to 6 (250 cycles before overflow interrupt)
 T0IE = 1                        ' Enable TMR0 overflow interrupt 
 GIE = 1                         ' Enable global interrupts 

;Main Loop
WHILE 1 = 1                   ;Endless Loop
    FOR PPSN = 1 TO 6         ;loop for 6 pulses = 1/16th note
        HSERIN [serReceive]   ;receive MIDI message [BYTE]
        IF serReceive != $F8 THEN  ;see if it is a Clock message
            PPSN = PPSN - 1   ;if not, then decrease the loopcounter to really receive 6 before continuing
        ENDIF
    NEXT
    stopcnt = counterint      ;time it took to get 1/16th note
    counterint = 0            ;reset counter

    tel4 = tel3               ;*  shift into next var
    tel3 = tel2               ;*  ""
    tel2 = tel1               ;*  ""
    tel1 = tel0               ;*  ""
    tel0 = stopcnt            ;*  get latest counter length
    sum = tel0 + tel1 + tel2+ tel3 + tel4  ;*  sum up
    average = sum / 5         ;*  get the average
    
    IF btnInc = pressed AND PulseFreq < 16384 THEN   ;@20MHz, max freq=32767Hz (/2 = 16384)
        RippleScalar = RippleScalar * 2              ;double the scalar
    ENDIF
    IF btnDec = pressed AND PulseFreq > 2441 THEN    ;@20MHz, min freq=1221Hz (*2 = 2442)
        RippleScalar = RippleScalar / 2              ;halve the scalar
    ENDIF
    
    IF RippleScalar / 1024 = 0 THEN
        Scale1 = 1
        Scale2 = 1024 / RippleScalar  ;MAX=1:8 (@BPM=250)
    ELSE
        Scale1 = RippleScalar / 1024  ;MAX=16:1 (@BPM=20)
        Scale2 = 1
    ENDIF
    
    BPMx100 = 1868400/average                       ;1868400 experimental value
    sixteenthNPSx100 = 4 * (BPMx100 / 60)           ;4 1/16th notes in one beat (=1/4th note)
    PulseFreq = RippleScalar * sixteenthNPSx100/100 ;calculate the pulse out frequency
                                                    
    HPWM 1, 127, PulseFreq    ;Output the PWM on portB.3, 50% duty cycle with frequency of 'PulseFreq'
    
    ;PRINT AT 1, 1, "BPM: ", DEC3 BPMx100/100, "  ", DEC2 Scale1, ":", DEC2 Scale2   ;in case of LCD
    ;PRINT AT 2, 1, "Freq.: ", DEC8 PulseFreq                                        ;in case of LCD
WEND
    
END                           ;Einde programma

Met vriendelijke groet,

Ron Burgstede

Hoi Ron,
Ik zou graag je programma uitchecken. Ik doe veel met midiclocks, heb een PIC brander maar kan niet programmeren. Er zijn helaas niet zo veel PIC projectjes te vinden en ik ben erg benieuwd wat ik met jou program zou kunnen. Zou je me er meer over willen vertellen en evt schema en code delen.
Vriendelijk bedankt,
Roel