Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Atmega16 SPI wysyłanie i odbieranie - nie działa

wojtek_777 23 Kwi 2016 10:03 1104 5
  • #1 23 Kwi 2016 10:03
    wojtek_777
    Poziom 6  

    Witam
    Na wstępie powiem że nie jestem ekspertem w C.
    Pisze programik działający na Atmedze16. Mam dwa dwa układy Ram i RTC i muszę się z nimi dogadać przez SPI. I się nie dogaduje.
    Zeby zapisać cos i odczytać z Ramu muszę zapisać (dla zapisu) kod rozkazu - 8 bitów, adres komórki zapisywanej - 24 bity i dane - 8 bitów. Przy odczycie analogicznie rozkaz, adres i odczytać dane.
    Nie działa mi to. Na pewno coś źle robię.
    Na pewno ta komunikacja jest prosta.
    Może ktoś mi coś podpowie. Kod kawałka programu z wysyłaniem i odbieraniem danych.
    Jak wcześniej wspomniałem nie mam za dużo doświadczenia w pisaniu programów w C.
    Napisałem na razie o Ramie. Myślę że jak mi komunikacja ruszy to z RTC dam radę.
    Z góry dzięki za pomoc.

    Kod C

    Kod: c
    Zaloguj się, aby zobaczyć kod
    /*
    * Created: 2016-04-08 04:49:41

    */
    #include <avr/pgmspace.h>
    #include <stdlib.h>
    #include <avr/io.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <avr/wdt.h>

    #define F_CPU 11059200UL

    #define Max_buf 11 //ilosc danych odbieranych
    #define Buf_nad 255 //ilosc danych wysylanych


    #define On_RTC PORTB = (PORTB & 0b11111110);
    #define Off_RTC PORTB = (PORTB | 0b00000001);
    #define On_SRAM PORTB = (PORTB & 0b11111101);
    #define Off_SRAM PORTB = (PORTB | 0b00000010);
    #define On_TXD_Off_RXD PORTD = (PORTD | 0b00010000);
    #define On_RXD_Off_TXD PORTD = (PORTD & 0b11101111);

    #define R_Mapy 23 // rozmiar mapy
    #define adr_slave 1 //

    #define LA (1<<PB4) //SS (RCK)
    #define OE (1<<PB3)
    #define DATA (1<<PB5) //MOSI (SI)
    #define CLOCK (1<<PB7) //SCK (SCK)

    #define SHIFT_REGISTER DDRB
    #define SHIFT_PORT PORTB

    //****************************************************************************************
    // Deklaracje zmiennych
    //****************************************************************************************

    volatile unsigned int Dane_Modbus;
    volatile unsigned int Ilosc_R;
    volatile unsigned char K_03,L_03;
    volatile unsigned char aaaa = 3, bbb = 0x08;
    volatile unsigned char ADR_K = 0;

    volatile unsigned int ktory_rej = 0; //rejestr do r/w
    volatile unsigned char blad_ramki= 0; //błedy ramki
    volatile unsigned char funkcja = 0; // funkcja modbus
    volatile unsigned char uart_str_complete = 0; // 1 .. String komplett empfangen
    volatile uint8_t uart_str_count = 0;
    volatile unsigned int licz_przep = 0;
    volatile uint8_t flaga_nad = 0;
    volatile unsigned char flaga_err = 0;
    volatile unsigned char licz_wys = 0; //ilosc danych w ramce

    //****************************************************************************************




    // Deklaracje tablic
    //****************************************************************************************
    volatile unsigned char Ramka[Max_buf] = "";
    volatile unsigned char Ramka_Zwrotna[Buf_nad] = "";
    unsigned int Mapa[R_Mapy];

    //****************************************************************************************
    // Deklaracje Funkcji
    //****************************************************************************************
    static void spi_masterinit(void);
    static void spi_write(volatile unsigned char data);
    static unsigned char spi_read(void);

    static void USART_Init(unsigned int baud,unsigned char par,unsigned char stop);
    int konfiguracja(void);

    static unsigned char sprawdz_ramke(void);
    static void nadaj_ramke(void);
    static void funkcja_3(void);
    static void przygotuj_ramke_err(volatile unsigned char blad);
    static unsigned short ModbusCRC(volatile unsigned char * buf, int size);

    int main(void)
    {


    volatile unsigned char blad;
    wdt_enable(WDTO_15MS );
    cli();

    konfiguracja();
    spi_masterinit();

    USART_Init(9600,2,1);

    wdt_reset(); // reset WatchDog'a
    sei();

    On_SRAM; // CS Ramu na niski
    aaaa = 1;
    spi_write(aaaa); // write mode
    Off_SRAM;
    On_SRAM;
    aaaa = 0;
    spi_write(aaaa); // po bajcie
    Off_SRAM; // CS Ramu na wysoki

    On_SRAM;
    aaaa = 5;
    spi_write(aaaa); // read mode
    Off_SRAM;
    On_SRAM;
    aaaa = 0;
    spi_write(aaaa); // po bajcie
    Off_SRAM;

    aaaa = 2;
    bbb = 123;
    On_SRAM;
    spi_write(aaaa);
    //Off_SRAM;
    //On_SRAM;
    aaaa = 0;
    spi_write(aaaa);
    spi_write(aaaa);
    spi_write(aaaa);
    Off_SRAM;

    On_SRAM;
    spi_write(bbb);
    Off_SRAM;

    aaaa = 3;
    On_SRAM;
    spi_write(aaaa);
    //Off_SRAM;
    //On_SRAM;
    aaaa = 0;
    spi_write(aaaa);
    spi_write(aaaa);
    spi_write(aaaa);
    //Off_SRAM;
    //On_SRAM;
    Mapa[0] = spi_read();
    Off_SRAM;

    while(1)
    {
    /* On_RTC;
    spi_write(aaaa);
    spi_write(bbb);
    Mapa[0] = spi_read();
    Off_RTC;
    */

    //glowna petla programu
    wdt_reset(); // reset WatchDog'a
    if (flaga_nad == 1)
    {
    flaga_nad = 0;
    if (uart_str_complete == 1)
    {
    blad=sprawdz_ramke();
    if (blad == 0)
    {
    if (funkcja==3)
    {
    funkcja_3();
    nadaj_ramke();
    }
    }
    if (blad != 0)
    {
    przygotuj_ramke_err(blad);
    nadaj_ramke();
    }
    uart_str_complete = 0;
    }
    }
    }
    }

    //-------------------------------------------------------------------------------------------------
    //
    // Definicje funkcji
    //
    //-------------------------------------------------------------------------------------------------
    //-------------------------------------------------------------------------------------------------
    // Funkcja inicjująca SPI
    //-------------------------------------------------------------------------------------------------
    void spi_masterinit(void)
    {
    //ustaw MOSI,SCK jako wyjscia
    SHIFT_REGISTER |= (DATA | LA | CLOCK | OE); //Set control pins as outputs
    SHIFT_PORT &= ~(DATA | LA | CLOCK | OE); //Set control pins low
    SPCR = (1<<SPE) | (1<<MSTR); //Start SPI as Master
    SPSR = (1<<SPI2X);//podwojna predkosc
    SHIFT_PORT |=LA;
    }
    //-------------------------------------------------------------------------------------------------
    // Funkcja zapisująca przez SPI
    //-------------------------------------------------------------------------------------------------
    void spi_write(volatile unsigned char data)
    {
    wdt_reset(); // reset WatchDog'a
    SPDR=data;
    while(!(SPSR & (1<<SPIF)))
    {
    wdt_reset();
    } //czekaj na flage SPIF
    }
    //-------------------------------------------------------------------------------------------------
    // Funkcja czytająca przez SPI
    //-------------------------------------------------------------------------------------------------
    unsigned char spi_read(void)
    {
    wdt_reset(); // reset WatchDog'a

    while(!(SPSR & (1<<SPIF)))
    {
    wdt_reset();
    } //czekaj na flage SPIF
    return SPDR;

    }

    //-------------------------------------------------------------------------------------------------
    // Funkcja konfigurująca
    //-------------------------------------------------------------------------------------------------
    int konfiguracja(void)
    {
    DDRA = 0xff; // Ustawienienie portu A jako wyjścia
    PORTA = 0x00; // wyzerowanie portu A

    DDRB = 0b10111011; // Ustawienie rodzaju pinów portu B
    // PB0 (CS0 - wybór RTC) - wyjście
    // PB1 (CS1 - wybór SRAM) - wyjście
    // PB2 (INT2 - impulsy z kółka) - wejście
    // PB3 (WOLNE) - wyjście
    // PB4 (WOLNE) - wyjście
    // PB5 (MOSI - SPI) - wyjście
    // PB6 (MISO - SPI) - wejście
    // PB7 (SCK - SPI) - wyjście
    PORTB = (PORTB & 0b11100111); // Wyzerowanie bitów PB#, PB4
    PORTB = (PORTB | 0b00000011); // Ustawienie bitów PB0 - CS0, PB1 - CS1

    DDRC = 0b10010100; // Ustawienie rodzaju pinów portu C
    PORTC = (PORTC & 0b01101011); // Wyzerowanie bitu PC7 - LED-programowanie układu

    DDRD = 0b11000010; // Ustawienie rodzaju pinów portu D
    // PD0 (RXD - USART) - wejście
    // PD1 (TXD - USART) - wyjście
    // PD2 (INT0 - zanik nap. zasilania) - wejście
    // PD3 (INT1 - przerwanie od RTC) - wejście
    // PD4 (Kierunek transmisji - 1-TXD, 0-RXD) - wyjście
    // PD5 (Switch - nastawy fabryczne) - wejście
    // PD6 (LED - nastawy fabryczne) - wyjście
    // PD7 (WOLNE) - wyjście
    PORTD = (PORTD & 0b00101111); // Wzerowanie bitów PD7, PD6, PD4
    PORTD = (PORTD | 0b00000011); // Usatwienie bitów PD0, PD1

    Off_RTC;
    Off_SRAM;
    On_RXD_Off_TXD;


    return 0;
    }
    unsigned short ModbusCRC(volatile unsigned char * buf, int size)
    {
    unsigned short crc = 0xffff;
    int i;

    while(size--)
    {
    crc ^= *buf;
    buf++;
    for(i=0; i<8; i++)
    {
    wdt_reset();
    if(crc & 1)
    {
    crc >>= 1;
    crc ^= 0xA001;
    }
    else
    {
    crc >>= 1;
    }
    }
    }
    return crc;
    }

    unsigned char sprawdz_ramke(void)
    {
    volatile unsigned int crc,crc_ramki;
    blad_ramki=0;
    funkcja=0;

    ADR_K = adr_slave;

    crc = ModbusCRC(Ramka,6);
    crc_ramki = Ramka[7];
    crc_ramki = (crc_ramki << 8) + Ramka[6];
    if (crc == crc_ramki)
    {
    if (Ramka[0]==ADR_K)
    {
    if (Ramka[1] == 0x3)
    {
    funkcja=3;
    }
    }
    }
    else
    {
    blad_ramki = 2;
    }
    return blad_ramki;
    }

    void przygotuj_ramke_err(volatile unsigned char blad)
    {
    unsigned int crc;

    Ramka_Zwrotna[0] = adr_slave;

    Ramka_Zwrotna[1]=Ramka[1] | 0b10000000;
    Ramka_Zwrotna[2]=blad;
    crc = ModbusCRC(Ramka_Zwrotna,3);
    Ramka_Zwrotna[3]=crc;
    Ramka_Zwrotna[4]=crc>>8;
    licz_wys=5;
    }


    void funkcja_3(void)
    {
    unsigned int crc;

    Dane_Modbus = 0;
    K_03 = 0;
    L_03 = 0;

    Dane_Modbus = Dane_Modbus + Ramka[2];
    Dane_Modbus = Dane_Modbus << 8;
    Dane_Modbus = Dane_Modbus + Ramka[3];

    Ilosc_R = Ramka[4];
    Ilosc_R = Ilosc_R << 8;
    Ilosc_R = Ilosc_R + Ramka[5];

    Dane_Modbus = Dane_Modbus + Ilosc_R;
    if (Dane_Modbus <= R_Mapy)
    {
    L_03 = (Ramka[3]);

    Ramka_Zwrotna[0] = adr_slave;
    Ramka_Zwrotna[1] = funkcja;
    Ramka_Zwrotna[2] = (Ramka[5] * 2); // ilość przesyłanych bajtów
    licz_wys = 3;

    for (K_03 = 0;K_03 < Ramka[5];K_03 = (K_03+1))
    {
    Dane_Modbus = Mapa[L_03];
    Dane_Modbus = Dane_Modbus >> 8;
    Ramka_Zwrotna[(K_03 * 2) + 3] = Dane_Modbus;
    Dane_Modbus = Mapa[L_03];
    Dane_Modbus = Dane_Modbus << 8;
    Dane_Modbus = Dane_Modbus >> 8;
    Ramka_Zwrotna[(K_03 * 2) + 4] = Dane_Modbus;
    licz_wys=(licz_wys + 2);
    L_03 = (L_03 + 1);
    }

    licz_wys=(licz_wys+2);
    crc = ModbusCRC(Ramka_Zwrotna,licz_wys-2);
    Ramka_Zwrotna[licz_wys-2] = crc;
    Ramka_Zwrotna[licz_wys-1] = crc >> 8;
    }
    else
    {
    Ramka_Zwrotna[1] = 0x83;
    Ramka_Zwrotna[2] = 2;
    licz_wys = 5;
    crc = ModbusCRC(Ramka_Zwrotna,licz_wys - 2);
    Ramka_Zwrotna[licz_wys - 2] = crc;
    Ramka_Zwrotna[licz_wys - 1] = crc >> 8;
    }


    }


    void nadaj_ramke(void)
    {
    volatile unsigned char k;
    wdt_reset(); // reset WatchDog'a

    On_TXD_Off_RXD; //ustawimay pin D2 w stan niski max 485 w trybie nadawania

    _delay_loop_1(10); //10us
    for (k=0;k<licz_wys+1;k++)
    {
    wdt_reset(); // reset WatchDog'a
    while ((UCSRA & (1 << UDRE)) == 0) {} // reset WatchDog'a}; // Do nothing until UDR is ready for more data to be written
    UDR = Ramka_Zwrotna[k]; // wysli bajt
    }
    while ((UCSRA & (1 << UDRE)) == 0) {}

    On_RXD_Off_TXD; // ustawimay pin D4 w stan wysoki max485 w trybie odbierania

    _delay_loop_1(10);
    }


    //-------------------------------------------------------------------------------------------------
    // Funkcja ustawiająca USART i licznik T0
    //-------------------------------------------------------------------------------------------------

    void USART_Init(unsigned int baud,unsigned char par,unsigned char stop)
    {

    unsigned char p_s = 0;

    switch(par)
    {
    case 0:
    {
    par = (par & 0b00000000); // parzystośc wyłączona
    }
    break;
    case 1:
    {
    par = (par | 0b00000100); // nieparzystość
    }
    break;
    case 2:
    {
    par = (par | 0b00000110); //parzystość
    }
    break;
    }

    switch(stop)
    {
    case 1:
    {
    stop = 0; // 1 bit
    }
    break;
    case 2:
    {
    stop = 1; // 2 bity
    }
    break;
    }

    p_s = par | stop;

    UCSRA &= ~_BV(FE); // błąd ramki
    UCSRA &= ~_BV(DOR); // przekroczenie danych
    //UCSRA &= ~_BV(PE); // błąd parzystości
    UCSRA &= ~_BV(U2X); // ustalenie szybkości transmisji (podwójna czy pojedyncza)
    UCSRA &= ~_BV(MPCM); // komunikacja między procesorami

    UCSRB = 0b10011000; // RXCIE=1 - odblokowanie przerwania zakończenia RX
    // TXCIE=0 - zablokowanie przerwania zakończenia nadawania TX
    // UDRIE=0 - zablokowanie przerwania na pusty rejestr danych
    // RXEN=1 - odblokowanie odbiornika
    // TXEN=1 - odblokowanie nadajnika
    // UCSZ2=0 - rozmiar znaku
    // RXB8=0 - 8 bit danych odbieranych
    // TXB8=0 - 8 bit danych nadawanych

    switch(p_s)
    {
    case 0: // brak parzystości i 1 bit stopu
    {
    UCSRC = 0b10000110;
    }
    break;
    case 1: // brak parzystości i 2 bity stopu
    {
    UCSRC = 0b10001110;
    }
    break;
    case 4: // nieparzystości i 1 bit stopu
    {
    UCSRC = 0b10100110;
    }
    break;
    case 5: // nieparzystości i 2 bity stopu
    {
    UCSRC = 0b10101110;
    }
    break;
    case 6: // parzystość i 1 bit stopu
    {
    UCSRC = 0b10110110;
    }
    break;
    case 7: // parzystość i 2 bity stopu
    {
    UCSRC = 0b10111110;
    }
    break;
    }


    DDRD |= _BV(4); //PORTD.4 jako wyjście

    UBRRH = 0;
    UBRRL = (((F_CPU / (baud * 16UL))) - 1);

    //------------------------------------------------
    // Konfiguracja rejestrów dla timera T0 - MODBUS USART0
    //------------------------------------------------

    TCCR0|=(1<<CS00)|(1<<CS02)|(0<<CS01); // preskaler ck/1024
    TCNT0 = 0;
    TIMSK |= (1 << TOIE0); // aktywne przerwanie overflow

    }




    ISR(USART_RXC_vect)
    {
    wdt_reset(); // reset WatchDog'a
    Ramka[uart_str_count] = UDR;
    TCCR0|=(1<<CS00)|(1<<CS02)|(0<<CS01); // preskaler ck/1024
    TCNT0 = 0;
    licz_przep = 0;
    flaga_err=0;
    flaga_nad=0;
    uart_str_count++;
    //if (uart_str_count == Max_buf) {

    // uart_str_count = 0;
    // uart_str_complete = 1;
    // }
    }


    ISR(TIMER0_OVF_vect)
    {
    wdt_reset(); // reset WatchDog'a

    licz_przep ++;
    if (licz_przep >= 1)
    {
    TCCR0 = 0x00;
    licz_przep = 0;
    TCNT0 = 0;
    uart_str_count = 0;
    flaga_nad=1;
    uart_str_complete = 1;
    }
    }

    0 5
  • #2 23 Kwi 2016 11:19
    2675900
    Użytkownik usunął konto  
  • #3 24 Kwi 2016 04:02
    wojtek_777
    Poziom 6  

    Dodałem
    Są też noty katalogowe do układów.
    Kwarc atmegi 11.059200 MHz
    Analizując mój kod nie gańcie mnie za bardzo.

    0
  • #4 24 Kwi 2016 11:39
    slx
    Poziom 18  

    Nie rozdziela się funkcji czytających i zapisujących SPI. To nie są osobne byty, komunikacja zawsze przebiega w obie strony jednocześnie (dla takiej konfiguracji, jak tu - dwukierunkowy master).
    Skąd ma się wziąć SPI SCK przy funkcji spi_read(). Jeżeli Atmega jest Masterem SPI musi coś wysłać żeby odebrać.

    0
  • #5 24 Kwi 2016 11:54
    grko
    Poziom 33  

    Jeszcze wydaje mi się, że zapisujesz/odczytujesz spod adresu którego ta pamięć nie ma:

    Kod: c
    Zaloguj się, aby zobaczyć kod


    Czyli adres jest równy 0x020202 > 0x20000 = 128KB. W datasheet jest jeszcze opisana sekwencja zapisu/odczytu. Wydaje mi się, że po każdej powinieneś podnieść linię CS.

    0
  • #6 15 Cze 2016 03:40
    wojtek_777
    Poziom 6  

    Witam
    slx ma rację

    slx napisał:
    Jeżeli Atmega jest Masterem SPI musi coś wysłać żeby odebrać


    Musiałem jeszcze jakieś takty zegara wysyłać
    Wtsyłam i teraz działa

    0