Ovládání Led pásku - PORTG

Přeji hezký večer.
Hraji si s programovatelnými RGB Pololu LED páskami, kdde posláním 24-bit adresy se zvolí barva LED.
Knihovnu jsem se snažil napsat, ale nejsem tak dobrý, tak jsem nějakou stáhnul.
Funguje velice dobře až na jednu věc.
Používám ATMEGA64 a když to zkouším použít pro PORTA,B,C,D,E tak to funguje ale pro porty F a G ne.
Dočetl jsem se, že porty F a G jsou v takzvaných extended registrech, ale nevím jak to v knihovně změnit aby to fungovalo.
Zde je funkce, která to zpracovává.

[code]
/** The rgb_color struct represents the color for an 8-bit RGB LED.
Examples:
Black: (rgb_color){ 0, 0, 0 }
Pure red: (rgb_color){ 255, 0, 0 }
Pure blue: (rgb_color){ 0, 255, 0 }
Pure green: (rgb_color){ 0, 0, 255 }
White: (rgb_color){ 255, 255, 255} */
typedef struct rgb_color
{
unsigned char red, green, blue;
} rgb_color;

/** led_strip_write sends a series of colors to the LED strip, updating the LEDs.
The colors parameter should point to an array of rgb_color structs that hold the colors to send.
The count parameter is the number of colors to send.
This function takes about 1.1 ms to update 30 LEDs.
Interrupts must be disabled during that time, so any interrupt-based library
can be negatively affected by this function.
Timing details at 20 MHz (the numbers slightly different at 16 MHz and 8MHz):
0 pulse = 400 ns
1 pulse = 850 ns
“period” = 1300 ns
*/
void attribute((noinline)) led_strip_write(rgb_color * colors, unsigned int count)
{
// Set the pin to be an output driving low.
LED_STRIP_PORT &= ~(1<<LED_STRIP_PIN);
LED_STRIP_DDR |= (1<<LED_STRIP_PIN);

cli(); // Disable interrupts temporarily because we don’t want our pulse timing to be messed up.
while(count–)
{
// Send a color to the LED strip.
// The assembly below also increments the ‘colors’ pointer,
// it will be pointing to the next color at the end of this loop.
asm volatile(
“ld tmp_reg, %a0+\n”
“ld tmp_reg, %a0\n”
“rcall send_led_strip_byte%=\n” // Send red component.
“ld tmp_reg, -%a0\n”
“rcall send_led_strip_byte%=\n” // Send green component.
“ld tmp_reg, %a0+\n”
“ld tmp_reg, %a0+\n”
“ld tmp_reg, %a0+\n”
“rcall send_led_strip_byte%=\n” // Send blue component.
“rjmp led_strip_asm_end%=\n” // Jump past the assembly subroutines.

    // send_led_strip_byte subroutine:  Sends a byte to the LED strip.
    "send_led_strip_byte%=:\n"
    "rcall send_led_strip_bit%=\n"  // Send most-significant bit (bit 7).
    "rcall send_led_strip_bit%=\n"
    "rcall send_led_strip_bit%=\n"
    "rcall send_led_strip_bit%=\n"
    "rcall send_led_strip_bit%=\n"
    "rcall send_led_strip_bit%=\n"
    "rcall send_led_strip_bit%=\n"
    "rcall send_led_strip_bit%=\n"  // Send least-significant bit (bit 0).
    "ret\n"

    // send_led_strip_bit subroutine:  Sends single bit to the LED strip by driving the data line
    // high for some time.  The amount of time the line is high depends on whether the bit is 0 or 1,
    // but this function always takes the same time (2 us).
    "send_led_strip_bit%=:\n"

#if F_CPU == 8000000
“rol tmp_reg\n” // Rotate left through carry.
#endif
“sbi %2, %3\n” // Drive the line high.

#if F_CPU != 8000000
“rol tmp_reg\n” // Rotate left through carry.
#endif

#if F_CPU == 16000000
“nop\n” “nop\n”
#elif F_CPU == 20000000
“nop\n” “nop\n” “nop\n” “nop\n”
#elif F_CPU != 8000000
#error “Unsupported F_CPU”
#endif

    "brcs .+2\n" "cbi %2, %3\n"              // If the bit to send is 0, drive the line low now.

#if F_CPU == 8000000
“nop\n” “nop\n”
#elif F_CPU == 16000000
“nop\n” “nop\n” “nop\n” “nop\n” “nop\n”
#elif F_CPU == 20000000
“nop\n” “nop\n” “nop\n” “nop\n” “nop\n”
“nop\n” “nop\n”
#endif

    "brcc .+2\n" "cbi %2, %3\n"              // If the bit to send is 1, drive the line low now.

    "ret\n"
    "led_strip_asm_end%=: "
    : "=b" (colors)
    : "0" (colors),         // %a0 points to the next color to display
      "I" (_SFR_IO_ADDR(LED_STRIP_PORT)),   // %2 is the port register (e.g. PORTC)
      "I" (LED_STRIP_PIN)     // %3 is the pin number (0-8)
);

// Uncomment the line below to temporarily enable interrupts between each color.
//sei(); asm volatile("nop\n"); cli();

}
sei(); // Re-enable interrupts now that we are done.
_delay_us(80); // Send the reset signal.
}
[/code]

Chyba je určitě v tomto

         "I" (_SFR_IO_ADDR(LED_STRIP_PORT)),   // %2 is the port register (e.g. PORTC)
          "I" (LED_STRIP_PIN)     // %3 is the pin number (0-8)

Pomůžete prosím?

To znamená, že k těmto portům nepřistupuješ pomocí IO instrukcí (in, out, sbi, cbi), ale pracuješ s nimi jako s RAM pamětí.

To je hezké.
Zkoušel jsem tu funkci přepsat do C, ale evidentně to neumím napsat tak, aby to mělo to správné časování.
Zkoušel jsem to přes přerušení od časovače, dokonce jsem použil i _delay_us, ale pásek jsem sám nerozběhl.
Poradíte prosím, jak tu funkci přepsat tak aby to fungovalo i pro PortF a G?
Předpokládám, že se změní počet cyklů a bude se tam muset ještě něco upravit, ale na to už mé znalosti nestačí.

Jde o to nahradit instrukce SBI a CBI instrukcí ST a mělo by to být univerzální pro použití pro porty jak na extended registrech, tak na standartních, protože i k portům na standartních registrech lze přistoupit pomocí adresace přes paměť. S časováním by moc velký problém být neměl, protože oboje instrukce trvají 2 takty hodin. Jediné, co musíš udělat je předat adresu portu do některého z dvojregistrů (X, Y, Z) a připravit si hodnoty portu pro vynulování a nastavení daného bitu, protože CBI a SBI pracují s bitem a neovlivňují zbytek portu, kdežto ST zapisuje do celého portu. Nejsem kovaný v použití inline assembleru v Cčku, takže Ti tady ukážu, jak by měl assemblerovský zápis vypadat a snad Ti někdo poradí, jak to přelouskat do inline assembleru.

Příklad pro použití s dvojregistrem Z :

ldi ZH, high(PametovaAdresaPortu) ; ZH asi bude vždy 0x00 ldi ZL, low(PametovaAdresaPortu) ldi TempRegistr, (1<<PozadovanyBit) ; TempRegistr musí být R16 až R32, kromě použitých pro adresu portu. mov NulovaciRegistr, TempRegistr ; Registr, ve kterém bude hodnota, která se zapíše místo instrukce CBI mov NastavovaciRegistr, TempRegistr ; Registr, ve kterém bude hodnota, která se zapíše místo instrukce SBI ld TempRegistr, Z ; Načtení aktuální hodnoty portu or NastavovaciRegistr, TempRegistr eor NulovaciRegistr, NastavovaciRegistr ; V tuhle chvíli je připraveno vše k nahrazení instrukcí CBI a SBI
To byla příprava před vlastní smyčkou. Tu provedeš jenom jednou na začátku.

Ve vlastní smyčce :

[code]cbi %2, %3
nahradíš instrukcí
st Z, NulovaciRegistr

sbi %2, %3
nahradíš instrukcí
st Z, NastavovaciRegistr
[/code]

Assembler není problém, ale do jeho inline použití jsem zatím nějak neproniknul, ale snad jsem Tě alespoň nasměroval.