Wskaźnik na tablicę dwuwymiarową i podstawienie jej do funkcji.

0

Piszę, poprawiam bibliotekę do obsługi czcionki, aby zajmowała mniej miejsca w małym mikroprocesorze (atmega8), niektóre jej fragmenty używam dwukrotnie, przez co powstaje potrzeba podmienić zmienną tablicę z czcionką w zależności od literki.

Problem zapewne trywialny a powstał dlatego że nie jestem (jeszcze) wystarczająco zaznajomiony ze wskaźnikami.
Szukałem rozwiązania w internecie, jednak ostatecznie nie znalazłem, dlatego piszę na forum.

 		ch = s[j];
		for(uint8_t line=0;line<6;line++)
		{
			uint8_t templine=line;
			const uint16_t *adr;
			switch(ch)
			{
				case '1':
				if(line>2) templine=2;
				if(line>4) templine=3;
				adr=ssd1306xled_font31x47_1;      /// chcę wskazać tę tablicę
				break;
				
				case '2':
				adr=ssd1306xled_font31x47_2;     ///albo tę
				break;
			}
			
			ssd1306_setpos(x, y+line);
			ssd1306_send_data_start();
			for (uint8_t i = 0; i < 32; i++) {
				ssd1306_send_byte(pgm_read_byte(*adr[templine][i]));    ///żeby była argumentem dla tej funkcji
				///przed zmianami było ssd1306_send_byte(pgm_read_byte(&ssd1306xled_font31x47_1[templine][i]));
			}
			ssd1306_stop();
		}

Wydaje mi się, że jest to możliwe, jeżeli nie, będę kombinował w inny sposób

1

Aby to zrobić Twoje adr musi być odpowiedniego typu.

Pokaż deklaracje pgm_read_byte i ssd1306xled_font31x47_1

0

Przestrzeń adresowa Atmegi8 jest 16bitowa, stąd też zadeklarowałem zmienną uint16_t.

#define pgm_read_byte(address_short)    pgm_read_byte_near(address_short)

/** \ingroup avr_pgmspace
    \def pgm_read_word(address_short)
    Read a word from the program space with a 16-bit (near) address. 

    \note The address is a byte address. 
    The address is in the program space. */ 

edit. jeszcze to się przyda

#define pgm_read_byte_near(address_short) __LPM((uint16_t)(address_short))

/** \ingroup avr_pgmspace
    \def pgm_read_word_near(address_short)
    Read a word from the program space with a 16-bit (near) address. 
    \note The address is a byte address. 
    The address is in the program space. */
 

co prowadzi do:

 #define __LPM(addr)         __LPM_classic__(addr)

a kończy się na assemblerze:

 #define __LPM_classic__(addr)   \
(__extension__({                \
    uint16_t __addr16 = (uint16_t)(addr); \
    uint8_t __result;           \
    __asm__ __volatile__        \
    (                           \
        "lpm" "\n\t"            \
        "mov %0, r0" "\n\t"     \
        : "=r" (__result)       \
        : "z" (__addr16)        \
        : "r0"                  \
    );                          \
    __result;                   \
}))
 const uint8_t ssd1306xled_font31x47_1 [4][32] PROGMEM = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0xC0,0xC0,0xE0,0xF0,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
//00000000,00000000,00000000,00000000,
//00000000,00000000,00000000,00000000,
//00000000,00000001,11111100,00000000,
//00000000,00000001,11111100,00000000,
//00000000,00000011,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00011111,11111100,00000000,
//00000011,11111111,11111100,00000000,
{0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
//00000011,11111111,11111100,00000000,
//00000001,11111111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x30,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x30,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00000111,11111100,00000000,
//00000000,00001111,11111110,00000000,
//00000000,00011111,11111111,00000000,
//00000000,00000000,00000000,00000000,
//00000000,00000000,00000000,00000000,

};

edit. Przy kompilacji wyskakuje

subscripted value is neither array nor pointer nor vector	GccApplication2	C:\Users\Pawel\Documents\Atmel Studio\7.0\GccApplication2\GccApplication2\ssd1306xled31x47.c	78
 

przy czym 78 linijka to ssd1306_send_byte(pgm_read_byte(*adr[templine][i]));

2

Jeżeli chcesz pokazywać na tablicę to musisz mieć wskaźnik na tablicę. A nie po prostu liczbę.

Może ten przykład Ci pomoże:

typedef char Sign[2][2];
 
void printSign(const Sign *const sign) {
  printf("%c%c\n", (*sign)[0][0], (*sign)[0][1]);
  printf("%c%c\n", (*sign)[1][0], (*sign)[1][1]);
}
 
int main(void) {
  const Sign signA = { {'x', 'o'}, {'o', 'x'} };
  const Sign signB = { {'o', 'x'}, {'x', 'o'} };
 
  const Sign *signPtr = &signA;
  printSign(signPtr);
 
  printf("\n");
 
  signPtr = &signB;
  printSign(signPtr);
}

http://ideone.com/OwFYHE

1

*adr[templine][i]) nie masz tutaj czasem błędu kompilacji? Jeżeli dobrze rozumiem Twój problem, to zamiana * na & powinna rozwiązać problem.

0

Dziękuję serdecznie za przykład, rozgryzłem to wreszcie :)

Ostatecznie funkcja wygląda tak:

void ssd1306_string_font31x47xy(uint8_t x, uint8_t y, const char s[]) {
	uint8_t ch, j = 0;
	while (s[j] != '\0') {
		ch = s[j];
		for(uint8_t line=0;line<6;line++)
		{
			uint8_t templine=line;
			uint16_t adr;

			switch(ch)
			{
				case '1':
				if(line>2) templine=2;
				if(line>4) templine=3;
				adr=&ssd1306xled_font31x47_1;
				break;
				
				case '2':
				adr=&ssd1306xled_font31x47_2;
				break;
				
				/* pozostało tylko dokonczyć czcionkę, ale zrobię to tylko dla cyfr, tylko tyle potrzebuję, a oszczędzę pamięć */
			}
			
			ssd1306_setpos(x, y+line);
			ssd1306_send_data_start();
			for (uint16_t i = 0; i < 32; i++) {
				ssd1306_send_byte(pgm_read_byte((adr+ templine*32 +i)));
			}
			ssd1306_stop();
		}
		
		x += 32;
		j++;
	}
} 

Ciekawostka, gdy adr deklarowałem jako static uint16_t *adr; to gdy adr=687, adr+5=697, czyli dodawał 2 razy więcej, przez co miałem problemy z obrazem, był ściśnięty w poziomie...
Po zmianie na uint8_t dodawał już dobrze, jednak nie można zaadresować na 8 bitach, dopiero jako uint16_t adr; zadziałało jak należy.
Ma ktoś pomysł dlaczego się tak działo? Na pewno ma to związek z długością zmiennej, ale nie mam pomysłu dlaczego tak się działo.

1

To się nazywa "arytmetyka pointerów". Kompilator zna rozmiar każdego typu w czasie kompilacji i odpowiednio zwiększa wskaźnik, żeby następny wskaźnik zawsze wskazywał na następny element. Przy samym uint16_t, nie ma arytmetyki pointerów bo to zwykły int jest, gdzie wrzucasz adres. Działa to, bo pgm_read_byte oczekuje wskaźnika i kompilator rzutuje int na wskaźnik. Prawdopodobnie kompilujesz bez flagi -Wall, bo w tym miejscu powinieneś dostać warning, bo nie ma jawnego rzutowania int na wskaźnik.

struct s
{
  uint32_t a;
  uint32_t b;
};

int main(void)
{
  uint8_t  *a = NULL;
  uint16_t *b = NULL;
  uint32_t *c = NULL;
  struct s *d = NULL;

  ++a; /* a == 0x01 */
  ++b; /* b == 0x02 */
  ++c; /* c == 0x04 */
  ++d; /* d == 0x08 */
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1