USI/SPI - nota AVR

0

Witam

Zwracam się z prośbą o wyjaśnienie z jaką maksymalną częstotliwością może być taktowany moduł USI ustawiony w tryb SPI na procku AtTiny25 AVR ?
Oraz czy przy takich ustawieniach :

USICR |=  (1<<USIOIE) | (1<<USIWM0);
USIDR = zmienna;

układ będzie mi wysyłał dane przy założeniu że zrobię tak :

USICR |= (1<<USICLK);

Czy ten ostatni rozkaz musi być ośmiokrotnie powtórzony aby cała dana wyszła na SPI ?
Czyli inaczej, czy bit USICLK rejestru USICR automatycznie się wyzeruje po wystawieniu kolejnego bitu na SPI ?

0

Jako spi master, jesteś odpowiedzialny za zegar - czyli machanie pinem clock. Niektóre mikrokontrolery jak stm po zapisaniu 1 bajtu do swojego rejestru dr (data register) od razu plują 8 taktów zegara. Musisz sprawdzić w nocie katalogowej swojego procka czy peryferium automatycznie wysyła clock czy nie. Biorąc jednak pod uwagę, że masz dostęp do pinu clock, spodziewam się, że musisz to robić ręcznie. W takim przypadku musisz wysłać 8 cykli zegara (pinup oraz pindown). Jednakże to wciąż może być konfigurowalne zachowanie i układ może pracować i tak i tak. Nota katalogowa powinna rozwiać wątpliwości.

0

16x, nie 8, bo generujesz zbocze na zmianę narastające i opadające. USICLK kontroluje rejestr przesuwający (co drugi takt), musisz przełączać USITC dla sterowania zegarem.

Datasheet str. 111.

0

Dziękuję kolegom.
Jeszcze tego co koledzy napisali nie przeanalizowałem ale od razu dziękuję.

Mam jeszcze jedno pytanie. Otóż kompilator AVR-GCC którego używam pluje mi następującym błędem:
"error: braced-group within expression allowed only inside a function"
na następujący fragment kodu:

#include <avr/pgmspace.h>
const char *xxx = PSTR("napis");

A miało być pięknie ponieważ zgodnie z dokumentacją makro PSTR powinno mi utworzyć stringa w pamięci programu i zwrócić do niego adres który później mogę wykorzystać tak :

char firstchar;
firstchar = pgm_read_byte (xxx);

Pierwsze pytanie dotyczy tego błędu. Skąd ten błąd ?
Drugie pytanie brzmi : jak odczytać drugi znak tego stringa stosując funkcję pgm_read_byte() ?
Trzecie pytanie brzmi : jaka jest różnica między definicją z użyciem PSTR a takim czymś :

const char *xxx PROGMEM = "napis";

i dlaczego nie mogę odczytać pierwszego znaku w taki sposób:

firstchar = xxx[0];

Dlaczego muszę stosować funkcję pgm_read_byte() zamiast po prostu wyciągnąć daną spod danego wskazania (tzn. czuję że kompilator trzeba w jakiś sposób poinformować że tu i teraz odwołujemy się do takiej i takiej zmiennej ale że ona leży we flashu a nie w RAMie z tym tylko że kompletnie patrząc na listing rozwinięcia tej funkcji pgm_read_byte() nie rozumiem jej działania ponieważ jej definicja sprowadza się do analizy jakiś wstawek assemblerowych zaszytych w makrach.

Chciałbym po prostu poznać mechanizmy rządzące tymi sytuacjami. Proszę o pomoc kolegów.

1

W skrócie: PSTR służy do tworzenia stringów natychmiastowych, czyli np. uart_puts(PSTR("debug: f1"));. Jak chcesz stringa we flashu, robisz __attribute__((__progmem__)) const char for[] = "bar"; Dlaczego tak: obejrzyj pgmspace.h i zobacz jak się to makro rozwija.

Więcej o użyciu tego tu:
http://www.avrfreaks.net/forum/tut-c-gcc-and-progmem-attribute?page=all

Co do pgm_read_xxx:
Pamięć operacyjna (RAM) i programu (FLASH) nakładają się na siebie adresami (Atmegaxxx Memories w datasheet). Do której z tych pamięci się odwołujesz określają odpowiednie rozkazy. W gcc defaultowo odnosisz się do pamięci operacyjnej, zresztą żeby cokolwiek zrobić z danymi z flasha i tak musisz wrzucić je do RAM/rejestru. Stąd pgm_read_xxx to tak naprawdę wywołanie odpowiednio opakowanego i zaadresowanego LPM, bo po prostu inaczej do flasha się dobrać nie można.

0

Wygląda na to że odpowiedziałeś mi na wszystkie nurtujące mnie pytania w tym zakresie używając kilku prostych słów. Super. Dziękuję Ci niezmiernie.

Mam trochę pytanie z innej beczki : czy Ty może masz na imię Grzegorz ? Bo chyba wydaje mi się że Cię znam. Ale może mi się wydaje tylko....

0

Weź napisz jeszcze takie coś :

Czemu mogę zrobić tak :

char *x = "Adam";

A już nie mogę tak :

uint8_t *z = {1, 2, 3};

... tzn. mogę ale jak się później do tego wskaźnika "z" odwołuję chcąc pozyskać dane spod tego wskazania to mi się program resetuje (wnioskuję że do tego wskaźnika wpisałem bzdury ale jednak coś... pytanie właśnie co...)
... czy to ma coś wspólnego z "l-wartością" i "r-wartością" ?

Prosiłbym o profesjonalne wyjaśnienie tego powyżej.
I jeszcze jedno pytanie : czemu niektóre kompilatory jeśli piszemy z przedrostkiem "const" :

const char *x = "Adam";

tworzą tego stringa we flashu i nie przepisują na starcie do RAMu a niektóre (jak np.AVR-GCC) utworzą go we flashu i na starcie przepiszą do RAMu.
Czy ma to jakikolwiek związek z rodzajem architektury (von neumanna / harvardzka) czy to tylko rodzaj kompilatora ma na to wpływ ?

1

W temacie uint8_t *z = {1, 2, 3};
http://stackoverflow.com/questions/8108416/excess-elements-of-scalar-initializer-for-pointer-to-array-of-ints
char *foo = "bar"; to co innego, immediate string (chyba tak to się nazywało) to nie to samo co array initialization.
Zwróć uwagę, że uint8_t z[] = {1, 2, 3}; zadziała ok.

Co do tego co robią kompilatory: o ile pamiętam (mogę się tutaj mylić) takie CortexM mogą czytać z flasha tak samo jak z RAM dzięki odpowiedniej organizacji adresów. W AVR użycie PSTR daje tylko tyle, że musisz manualnie wciągnąć tego stringa do RAM, przez co nie zajmuje on tam miejsca cały czas i jest to oszczędność w sekcjach inicjowanych przy starcie kosztem użycia stosu lub sterty.
BTW - const nie gwarantuje, że cokolwiek będzie we flashu, bo siłą rzeczy może to dotyczyć wyłącznie compile-time constants (wyliczanych w czasie kompilacji). Od tego w C++ masz constexpr.

I nie, nie Grzegorz nawet nie na drugie. ;)

0

Dziękuję bardzo.
W celu uzupełnienia znalazłem jeszcze taki artykuł do stringów:

http://stackoverflow.com/questions/6185217/memory-session-used-to-store-immediate-strings-in-c/6185257

Rozpisano w nim do jakich sekcji pamięci trafią jakie dane.
W skrócie :

  • zmienne inicjalizowane globalne : do .data
  • inicjalizowane wskaźniki : do .rodata
  • zmienne niezainicjalizaowane globalne : do .bss (zgodnie ze standardem powinny być zerowane automatycznie na początku) albo .noinit (jeśli deklarowane z atrybutem "noinit")
  • stringi : zazwyczaj do .text
  • zmienne lokalne : w runtimie na stosie w RAMie

Jeśli flash jest podzielony na .text (kod programu + stringi) i .data (wartości inicjalizacyjne zmiennych globalnych) to w czasie startu kopiowane jest z flash do RAM jak poniżej:
-stringi z .text do .data
-z .data do .data

Miałbym jeszcze tylko pytanie dot. liczb zmiennoprzecinkowych.
Zapis jest taki :

sign * Mantysa * 2 ^ wykładnik

Załóżmy że liczymy dla 0x C3 96 00 00 i załóżmy że pierwszy bit to znak, kolejnych osiem to wykładnik, kolejnych 23 to mantysa.
Mam pytanie co do obliczenia wykładnika w takim zapisie. Ile on wynosi i jak go policzyć (mam pewną niezgodność z literaturą dlatego tutaj pytam).

Przydałoby się również wiedzieć jak napisać prosty podprogram w języku ANSI C któryby wyświetlał każdy z 32 bitów zdefiniowanej liczby float ?
Próbowałem z printf'em i rzutowaniem ale nic mi nie wyszło jak na razie z tych prób. Jak wystawić tą liczbę na bitach ?

Pomocy proszę.

0

Ok dzięki wszystkim za chęć pomocy.
Reasumując: reprezentację bitową float'a możemy wyświetlić np. tak :

    /* wyświetlenie liczby float na bitach */
    int8_t i,j;
    float test = 5.5;
    char * p = &test;
    /* petla iteruje w dol poniewaz endianess kompilatora MINGW jest typu little endian */
    for (i=3; i>-1; --i)
    {
        uint8_t dec_1byte = *(p+i);
        printf("\nWartosc %d bajtu liczby float wynosi : %d\n",4-i,dec_1byte);

        /* wewnetrzna petla iterujaca po kazdym bicie danego bajtu */
        for (j=0; j<8; ++j)
            printf(" %d",(dec_1byte>>(7-j))&1);

    }

Liczba taka jest zapisywana tak :
float = znak * M * 2 ^ E
gdzie E jest znormalizowanym wykładnikiem. Dołączam dodatkowo calculator liczb float/binary oraz artykuł dot. sposobu zapisu liczby E :
https://www.h-schmidt.net/FloatConverter/IEEE754.html
https://pl.wikipedia.org/wiki/Kod_z_przesuni%C4%99ciem

Pozdrawiam i dzięki za pomoc.

0

Nie możemy, to jest UB. Albo używasz unii w C99 albo memcpy.

0

Nie bardzo rozumiem dlaczego nie.
Zaprogramowałem proponowany przeze mnie fragment kodu w języku C na PC-cie w Eclipse używając MinGW i wyświetla mi te bity po kolei. Jestem pewien że to poprawna reprezentacja float'a ponieważ sobie to na kartce rozpisałem i policzyłem a dodatkowo skorzystałem z kalkulatora "do float'ów".

Oczywiście zgadzam się z tym że unia byłaby lepszym rozwiązaniem bo prostszym ale czemu piszesz że nie możemy zrobić tak jak ja zrobiłem ?
I w jaki sposób byś to proponował zrobić z użyciem memcpy() - rozwiń proszę bo ja zrobiłem tak że :

  • zdefiniowałem strukturę z 32 polami bitowymi (po kolei licząc od 1 do 32)
  • instrukcją memcpy skopiowałem 4 bajty spod adresu tego float'a do adresu mojej struktury
  • wyświetliłem wszystkie pola bitowe

O dziwo wynik jaki otrzymałem był zgodny z tym co robiłem poprzednio ale wyciągam dwa zasadnicze wnioski :

  1. Zapis jako little endian
  2. Bit numbering LSB0
    Najstarsze bity danych bajtów były w polach bitowych o numerach : 8, 16, 24 i 32 (a nie jak mogłoby się wydawać 1, 9, 17, 25). Orócz tego (little endian) mamy tak że np. bajt złożony z pól bitowych o numerach 8, 7, 6, 5, 4, 3, 2, 1 jest bajtem najmłodszym tej liczby!
1

Mój błąd. char* jest specyficzny, z nim możesz tak teoretycznie zrobić. Jakbyś chciał to wyświetlić w grupach po 16 czy 32 bity to już będzie to UB:
http://stackoverflow.com/questions/17789928/whats-a-proper-way-of-type-punning-a-float-to-an-int-and-vice-versa
W temacie memcpy drugi post z linka powyżej;)

http://stackoverflow.com/questions/262379/when-is-char-safe-for-strict-pointer-aliasing

Ale żeby z tym charem za pięknie nie było:
http://blog.qt.io/blog/2011/06/10/type-punning-and-strict-aliasing/

Generalnie musiałbym do standardu spojrzeć ;)

0

Dziękuję Ci bardzo kolego.
Już rozumiem co miałeś na myśli mówiąc o tym rzutowaniu.
Tak dla uzupełnienia : generalnie to takie rzutowanie przechodzi ale z "warning'ami" bo w C praktycznie zrobić się da wszystko, ale już nie wszystko jest bez warning'ów i nie wszystko ma sens.

Mam jeszcze jedno pytanie:

  • jeśli piszesz "UB" masz na myśli stricte unię czy też strukturę z polami bitowymi ?
    Jeśli masz na myśli strukturę to rozumiem że rzutowanie na własny typ zdefiniowany jako typ strukturalny z polami bitowymi będzie również niezgodny ze standardem ?

Dlaczego zatem dla następującego kodu implementowanego dla mikrokontrolerka AVR nie ma warningów ? :

typedef struct
{
    volatile uint8_t bit_0 : 1;
    volatile uint8_t bit_1 : 1;
    volatile uint8_t bit_2 : 1;
    volatile uint8_t bit_3 : 1;
    volatile uint8_t bit_4 : 1;
    volatile uint8_t bit_5 : 1;
    volatile uint8_t bit_6 : 1;
    volatile uint8_t bit_7 : 1;
} port;

#define PORTA   _SFR_IO8(0x1B)

#define LED_RX_BUF_OVF (((volatile port *)(&PORTA)) -> bit_1)

Przecież tutaj też odbywa się rzutowanie adresu rejestru na coś innego niż adres rejestru (bo na typ wskaźnika do struktury)..
Wiem że ta dyskusja to przesada jednak bardzo ciekawy jestem jak to tutaj jest.

0

Pokaż jeszcze jak wygląda _SFR_IO8

0

Wygląda to wszystko tak:

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)
#define __SFR_OFFSET 0x20
#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

#define PORTA   _SFR_IO8(0x1B)
#define LED_RX_BUF_OVF (((volatile port *)(&PORTA)) -> bit_1)

Po podstawieniu przez preprocesor musi być tak że :

#define LED_RX_BUF_OVF (((volatile port *)(&((*(volatile uint8_t *)(0x1B))+0x20))) -> bit_1)

Po pierwsze: sory za tyle nawiasów ale już mi się tego nie chciało poprawiać.
Jak widać po podstawieniu to chodzi o rzutowanie typu wskaźnika na uint8_t na typ wskaźnika do struktury z ośmioma polami bitowymi. Niemniej jednak to dwa różne typy, a tutaj warningi nie występują.

Taka pierdoła ale do myślenia daje... :) Ci którzy są zajęci niech to proszę odpuszczą bo to pierdoła, chętnych proszę o pomoc w wyjaśnieniu dlaczego jeśli rzutuję adres float'a na wskaźnik do struktury z 32 polami bitowymi dostaję warninga: "initialization from incompatible pointer type" , a w przypadku takiego rzutowania co wg mnie sprowadza się do rzutowania adresu uint8_t na typ wskaźnika do struktury z ośmioma polami bitowymi to już tego warninga nie ma... czemu ?

1

W skrocie: bo char*, aczkolwiek nie jestem pewny czy ma to tez zastosowanie do unsigned, zachowuje się podobnie do void*, moze byc wskaźnikiem uniwersalnym. Taka pamiatka sprzed ansi c.

0

Aha. :) fajnie że mnie podszkoliłeś trochę, dziękuję Ci.
I wygląda na to że chyba ma ponieważ typ uint8_t jest zdefiniowany jako :

typedef unsigned char uint8_t;

Gdyby było inaczej to dostałbym warningi przy kompilacji. Prosty wniosek.

0

Chciałbym się jeszcze tylko podzielić wiedzą w temacie definiowania stringów we flash'u AVR'a.
Otóż zakładając że chcemy umieścić tam jednego tylko string'a to robimy to tak :

#include <avr/pgmspace.h>
const char str[] PROGMEM = "Ala ma kota.";

ale nie tak :

#include <avr/pgmspace.h>
const char *str PROGMEM = "Ala ma kota.";

...i mamy wówczas w Ram'ie jedynie wskaźnik do tej tablicy "char'ów" czyli do naszego stringa.
Gdybyśmy zrobili tak jak piszę aby nie robić to we flash'u byłby wskaźnik do tego stringa a sam string i tak byłby kopiowany do Ram'u.

Dodatkowo, jeśli jednak chcemy zdefiniować we flash'u kilka stringów a ich wskaźniki umieścić w tablicy wskaźników do pamięci flash która to tablica będzie przechowywana w Ram'ie to wówczas robimy to tak :

#include <avr/pgmspace.h>
const char str1[] PROGMEM = "Ala ma kota.";
const char str2[] PROGMEM = "Kot ma Alę.";
PGM_P str_tab[] PROGMEM = { str1, str2 };

...i wówczas mamy we flashu tablicę wskaźników do stringów umieszczonych również w pamięci flash.
Jednak jedna rzecz mi nie daje spokoju.
Mianowicie mając we flashu oprócz stringów również i tablicę wskaźników do nich w jaki sposób mogę dobrać się do wybranego stringa. W AVR z uwagi na harvardzką architekturę mamy tak, że musimy czytać z flash'a poprzez specjalną funkcję pgm_read_byte, gdzie jej argumentem będzie adres. Czy zatem w takim wypadku muszę to zrobić tak:

char pierwszy_znak_drugiego_stringa = pgm_read_byte(pgm_read_byte(str_tab[1]));

? Jeśli nie tak, to jak ?
I czy warto w ogóle trzymać wskaźniki do tych stringów również we flashu czy też może lepiej zrobić tak :

const char str1[] PROGMEM = "Ala ma kota.";
const char str2[] PROGMEM = "Kot ma Alę.";
PGM_P str_tab[] = { str1, str2 };

co spowoduje że cała ta tablica wskazań do stringów (które nota bene są trzymane we flashu) będzie trzymana w Ram'ie... ?
Proszę o odpowiedzi stricte na moje pytania. Pytania potrzebne mi są do programowania AVR-ów.

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