2020/06/01

Ancora sul codice

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 pullup
  • DDRD ← 0b00000000 8 bit PORTD input
  • PORTD ← 0b11111111 pullup
  • DDRC ← 0b00011111 PORTC out su PC0-4 x matrice tastiera
  • PORTC |= 0b00011111; 1 su PC0-4
  • ADMUX ← (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 result
  • ADMUX &= 0b11111000 input ADC0
  • ADMUX |= 0b00000101; input ADC PC5
  • TIMSK0 ← 0 disable all interrupts for Timer0
  • TIMSK2 ← 0 disable all interrupts for Timer2
  • WDTCSR ← 0 disabilita watchdog
  • PORTB out (PB0 INT, PB1,2 PWM)
    • DDRB ← 0b10111111; tutti output
    • PORTB &= 0b01000000 tutti 0 (bè, non esattamente tutti!)
  • EIMSK ← 0 disabilita INT0 e INT1
  • Abilitazione software INT da pinchange PCINT0
    • PCICR ← 0b00000001 abilita PCINT 0, disabilita interrupts bitchange PCINT 1,2
    • PCMSK0 ← 0b00000001 abilita PCINT0, disabilita bitchange da PCINT7 a PCINT1
      • Toggle pin: write 1 to PINxn in PORTxn(sbi(port,bit)  (port) |= (1 << (bit)))
      • Flag INT sarà bit 0 in PCIFR
  • PCMSK2 ← 0 disabilita bitchange da PCINT23 a PCINT16
  • PCMSK1 ← 0 disabilita bitchange da PCINT14 a PCINT8
  • TCCR1A ← 0
  • TCCR1B ← 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 using ICR1 as TOP
  • TCCR1B |= (1 << CS10) START the timer with no prescaler
  • Preset_on ← 3 blocca lettuta pot [sic]
  • PitchSet ← Pitch - PitchOff;
  • PitchNow ← Pitch; offset = attuale pot
  • ICR1TOP set TOP base
  • OCR1AMID set PWM for 50% duty cycle @ 16bit
  • OCR1BMID
  • TIMSK1 ← (1 << TOIE1) abilita INT 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 MID1:

/// 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.


  1. 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.