Verstehe Code nicht .. brauche Hilfe

Hallo Leute,

ich versuche gerade den Code für mein Theremin zu verstehen/optimieren.

Allerdings verstehe ich einfach nicht, wie und warum gewisse Codezeilen funktionieren.

Ich poste hier mal den Teil des Codes, der mir Schwierigkeiten bereitet.

Es gibt ein Array mit 1024 Einträgen, das ist die Wavetable (gekürzt):

include <avr/pgmspace.h>

const int16_t sine_table0[1024] PROGMEM = {\
2319,
2334,
2348,
2363,
2376,
2391,
2404,
2419,
2433,
2447,
2462,
2475,
2490,
2503,
2518,
2531,
2546,
usw ......

Diese Wavetable wird in einer Interrupt-Routine verwendet, im Endeffekt werden ausgewählte Einträge an den DAC gesendet …

/* General variables */
int32_t pitch_init       = 0;       // Initialization value of pitch
int32_t vol_init         = 0;       // Initialization value of volume
int32_t pitch_v,pitch_l;            // Last value of pitch (for filtering)
int32_t vol_v,vol_l;                // Last value of volume (for filtering)
uint16_t pitch           = 0;       // Pitch value
uint16_t pitch_counter   = 0;       // Pitch counter
uint16_t pitch_counter_l = 0;       // Last value of pitch counter
uint16_t vol             = 0;       // Volume value
uint16_t vol_counter_l   = 0;       // Last value of volume counter


/* volatile varables  - used in the ISR Routine*/
volatile uint8_t vol8;               // Volume byte
volatile bool flag_vol,flag_pitch        = 0;   // Volume read flag
volatile uint16_t vol_counter,vol_counter_i = 0;   // Volume counter
volatile uint16_t pointer     = 0;   // Table pointer
volatile uint16_t add_val     = 0;   // Table pointer increment
volatile uint16_t timer       = 0;   // Timer value
volatile uint8_t deb_p,deb_v       = 0;   // Counters vor debouncing


/* 16 bit by 8 bit multiplication */
static inline uint32_t mul_16_8(uint16_t a, uint8_t b)  
{
  uint32_t product;
  asm (
    "mul %A1, %2\n\t"
    "movw %A0, r0\n\t"
    "clr %C0\n\t"
    "clr %D0\n\t"
    "mul %B1, %2\n\t"
    "add %B0, r0\n\t"
    "adc %C0, r1\n\t"
    "clr r1"
: 
    "=&r" (product)
: 
    "r" (a), "r" (b));
  return product;
}

/* Externaly generated 31250 Hz Interrupt for WAVE generator (32us) */
ISR (INT1_vect)                         
{
  // Interrupt takes up a total of max 25 us
  EIMSK &= ~ (1<<INT1);     // Disable External Interrupt INT1 to avoid recursive interrupts
  interrupts();             // Enable Interrupts to allow counter 1 interrupts

  int16_t temp_val;         // temporary variable 1
  uint32_t temp2_val;       // temporary variable 2

  // Wie und warum funktioniert diese Konstrukt
  temp_val = (signed int)pgm_read_word_near (sine_table + ((unsigned int)(pointer>>6) & 0x3ff));  //Read next wave table value (3.0us)

  // Aber das nicht ?
  //temp_val = (signed int)pgm_read_word_near (sine_table0[pointer]);

  // multiply 16 bit wave number by 8 bit volume value (11.2us / 5.4us)

    temp2_val=mul_16_8(temp_val,vol8);
    temp2_val=temp2_val>>9; 
  
  mcpDacSend(temp2_val);        //Send result to Digital to Analogue Converter (audio out) (9.6 us)

  pointer = pointer + add_val;  // increment table pointer (ca. 2us)
  timer++;                      // update 32us timer

};

Es gibt natürlich noch mehr Code, aber im Moment interessiert mich nur dieser Teil.

Meiner Meinung nach kann das erste pgm_read_word_near( … ) keine sinvollen Einträge aus der Tabelle liefen, aber es funktioniert.

Das zweite pgm_read_word_near() sollte (nach Recherchen im I-Net) funktionieren ohne Tricks und doppelten
Boden , tut es aber nicht, da kommt nur Krach raus …

Verrückt ist auch, dass
pointer = pointer + add_val;

zu einem Überlauf führen sollte … macht es aber nicht, wie man so auf die 1024 Elemente des Arrays korrekt zugreifen kann ist mir ein Rätsel …

Vielleicht bin ich auch beriebsblind … wer weiß.

Es gibt noch mehr Code, aber keinen, der pointer betrifft und auich nicht das Array …
die Interupt-Routine habe ich natürlich gekürzt, da steht zwar noch einiges aber nichts,
was mit dem Array zu tun hat.

So, ich hoffe/weiß jemand von euch wird das Rätsel lösen.

Viele Grüße Harry

PS: Wenn ihr mehr Infos braucht, ich gebe alles was ich habe …

Du kannst mit PROGMEM sowieso nur 64k Flash adressieren, da Zeiger generell nur 16 Bit haben. Ein Überlauf findet da theoretisch irgendwann mal statt. Das ist korrekt. Aber es ist im Prinzip egal, da eh nicht mehr geht.

Was da gemacht wird lässt sich so schlecht sagen. Man sieht nicht wo pointer und add_val mal von 0 abweichen

Ach ja, ein großer Grund weshalb das so nie und niemals geht:

temp_val = (signed int)pgm_read_word_near (sine_table0[pointer]);

Du musst die Adresse des Wertes übergeben und nicht den Wert selbst!

Ob dass dann aber für die Anwendung korrekt geht kann ich nicht sagen. Irgendein Grund wird die Bit-Fummelei schon haben

add_val kommt im Prinzip “vom Theremin-Shield”, ein Wet >0, der die Tonhöhe definiert.
pointer wird nur in dieser ISR referenziert, einmal für die indizierung, danach wird
add_val zu pointer addiert , das ergibt den nächsten Wert für den DAC.

So wird der Add_val-Wert berechnet …(pitch sind die Werte die der Shield liefert)

add_val=(pitch_init-pitch_v)/2+200; break; // normal operation

Tut aber nichts zur Sache … add_val isteinfach nur ein int-Wert > 0

Grüße

Harry