Ho continuato a guardare il codice, serie I versione 8 (quello che va, o uno di quelli che vanno, con lo schema 8) un terreno a me un po’ più familiare.
La funzione setup()
eseguita una tantum può essere interessante anche per capire il resto (ferraglia inclusa); poi devo cercare quali sono i fuse usati.
setup(void);
Ok, il void
l’ho aggiunto io perché pochi lo fanno, e nel codice di mio padre in effetti manca. (Ma non ovunque… Probabilmente dipende da dove ha attinto per quel pezzo di codice.)
MCUCR
← 0 assicura abilitazione pullupDDRD
← 0b00000000 8 bitPORTD
inputPORTD
← 0b11111111 pullupDDRC
← 0b00011111PORTC
out suPC0
-4 x matrice tastieraPORTC
|= 0b00011111; 1 suPC0
-4ADMUX
← (1 <<REFS0
)|(1 <<ADLAR
) Ref Vcc,ADC
8 bit –>ADCH
ADCSRA
← 0b10000100 prescaler 16(ADPS2
),ADC
abilitato(ADEN
) (| 0b01000000 ←ADSC
start); (ADCSRA
& 0b01000000) =1 running, =0 ok conversion;ADCH
← 8bit resultADMUX
&= 0b11111000 inputADC0
ADMUX
|= 0b00000101; inputADC
PC5
TIMSK0
← 0 disable all interrupts for Timer0TIMSK2
← 0 disable all interrupts for Timer2WDTCSR
← 0 disabilita watchdogPORTB
out (PB0
INT
,PB1
,2PWM
)DDRB
← 0b10111111; tutti outputPORTB
&= 0b01000000 tutti 0 (bè, non esattamente tutti!)
EIMSK
← 0 disabilitaINT0
eINT1
- Abilitazione software
INT
da pinchangePCINT0
PCICR
← 0b00000001 abilitaPCINT
0, disabilita interrupts bitchangePCINT
1,2PCMSK0
← 0b00000001 abilitaPCINT0
, disabilita bitchange daPCINT7
aPCINT1
- Toggle pin: write 1 to
PIN
xn inPORT
xn(sbi(port,bit) (port) |= (1 << (bit))) - Flag
INT
sarà bit 0 inPCIFR
- Toggle pin: write 1 to
PCMSK2
← 0 disabilita bitchange daPCINT23
aPCINT16
PCMSK1
← 0 disabilita bitchange daPCINT14
aPCINT8
TCCR1A
← 0TCCR1B
← 0- set none-inverting mode su A, non inverting su B
TCCR1A
|= (1 <<COM1A1
)|(1 <<COM1B1
);//|(1 <<COM1B0
);TCCR1A
|= (1 <<WGM11
);
TCCR1B
|= (1 <<WGM12
)|(1 <<WGM13
) set Fast PWM mode usingICR1
asTOP
TCCR1B
|= (1 <<CS10
) START the timer with no prescaler- …
- Preset_on ← 3 blocca lettuta pot [sic]
- …
- PitchSet ← Pitch - PitchOff;
- PitchNow ← Pitch; offset = attuale pot
- …
ICR1
←TOP
setTOP
baseOCR1A
←MID
setPWM
for 50% duty cycle @ 16bitOCR1B
←MID
TIMSK1
← (1 <<TOIE1
) abilitaINT
OVF
sei()
Il miscuglio di commenti in italiano e inglese fa pensare che ci siano dei copia-incolla da codice di qualche progetto i cui commenti erano in inglese.
Per quanto riguarda TOP
e MID
1:
/// Parametri globali oscillatori
/// clock CPU 20 MHz -> 0.05 uec
/// periodo PWM (738 cicli)-> 36.9 uec = 27100.271 Hz
/// MID = 369, dinamica +/- 368. Con 6 voci, ogni sample +/- 61(.5)
Assembly assembly…
Versioni precedente sono solo C, ma aveva iniziato a rimpiazzare ampie porzioni con codice riscritto in assembly. Credo, ma non sono sicuro, che partisse dal codice già ottimizzato[^2] prodotto dal compilatore e limasse ancora qualcosa, per guadagnare qualche ciclo o qualche byte di spazio.
Mi sembra però di ricordare che una volta mi aveva detto (forse a valle di queste ottimizzazioni) che ora aveva abbastanza cicli per poter provare a implementare effetti più onerosi.
Chiaramente il codice assembly non è proprio il massimo della comprensibilità… In generale, ma anche e soprattutto perché non conosco affatto l’assembly di questo µC (e invero di nessun µC).
Tramite alcune define era possibile attivare o disattivare le parti riscritte in assembly.
Prima di cimentarsi in questa impresa, comunque, aveva già iniziato ad usare delle macro per fare alcuni conti onerosi (moltiplicazioni, mi sembra); le macro si trovano, almeno in questa versione di questa serie del codice, nel file x_macro.h
.
/// moltiplica int16 x uint8 --> result / 256 int16
/// versione ottimizzata con arrotondamento
#define M_S16xU8_SH16rnd(intRes, int16In, uint8In) \ …
// versione ottimizzata senza arrotondamento
#define M_S16xU8_SH16(intRes, int16In, uint8In) \ …
/// versione ottimizzata con arrotondamento
#define M_U16xU8_U16rnd(uintRes, uint16In, uint8In) \ …
// versione ottimizzata senza arrotondamento
#define M_U16xU8_U16(uintRes, uint16In, uint8In) \ …
#define M_U16xS8_S16(int16Res, uint16In, int8In) \ …
/// NOTA: Moltiplicazione di 3 uint8 e div finale per 1024
/// Il risultato delle moltiplicazioni non deve superare 16 bit (es 40*40*40)
/// versione ottimizzata con arrotondamento
#define M_U8xU8xU8_U8rnd(uint8Res, uint8a, uint8b, uint8c) \ …
/// versione ottimizzata senza arrotondamento
#define M_U8xU8xU8_U8(uint8Res, uint8a, uint8b, uint8c) \ …
// Sostituice il codice C:
/// LFO2 += dir_LFO2;
/// LFO_2 =(LFO2 * Set.amp_LFO2) / 256;
#define M_LFO(res, lfo, dir, amp) \ …
#define M_LFOrnd(res, lfo, dir, amp) \ …
A proposito delle versioni con e senza arrotondamento, mi ricordo che mi aveva detto che non sentiva troppa differenza, cioè che anche senza arrotondamento corretto la differenza era impercettibile in pratica (e quindi non valeva la pena sprecare istruzioni per fare l’arrotondamento giusto). Tuttavia vedo che ha conservato tutte e due le versioni.
L’uso di
///
invece dei soli//
per i commenti penso sia dovuto al fatto che///
veniva riconosciuto dall’editor che usava come commento doxygen, e colorato in modo diverso, forse più risaltante, rispetto ai commenti normali.↩︎
Nessun commento:
Posta un commento
Commenta solo per dare un contributo utile, una critica costruttiva o fare un'osservazione acuta. Non commentare solo per dire che esisti anche tu o che ti piace o dispiace quello che hai letto e visto su questo blog.