Laboratorní zdroj – 11. Slave modul ATmega16 UART

Popis programu, který prostřednictvím sériového kanálu UART na procesoru ATmega16 přijímá data z nadřízené desky Master, odesílá odpověď, nastavuje DA převodník DAC8563 a čte data z AD převodníku ADS8341.

DAC8562 ADS8343

 

Komunikaci mezi procesory řídí deska Master, takže sériový kanál procesoru ATmega16 čeká na data, která vyšle Master. Je detekován první znak ‘Z’, kterým začíná každý paket 20-ti bajtů. Předpokládá se, že na sériové sběrnici je připojeno větší množství Slave modulů. Proto je při příjnu kontrolován ještě čtvrtý znak, který označuje číslo modulu. Pokud jsou tyto dva znaky součástí přijatého paketu, bude přijato všech dvacet znaků do pole dat SerialArray.

Potom program vypočítá CRC přijatých znaků a výsledek porovná s CRC, který vypočítal Master a uložil na konec vysílaného paketu. Když jsou oba CRC shodné, předpokládá se, že zpráva je bez chyb a data budou použita k ovládání Slave modulu.

Ihned po dokončení příjmu dat a vyhodnocení jeho správnosti jsou do pole dat SerialArray uloženy hodnoty, které bude Slave modul vysílat okamžitě vysílat. Předpokládá se, že Master po odeslání zprávy nechá dostatečný prostor na sběrnici a jiný modul nebude vysílat. Takže Slave nekontroluje, zda je sběrnice volná. Součástí vysílané zprávy jsou data naměřená AD převodníkem a CRC vysílaných dat.

Celá komunikace je obsluhována programy přerušení od sériového kanálu.

Hlavní program procesoru na začátku nastaví hodnoty registrů, které ovládají periferní zařízení, I/O porty procesoru, sériový kanál, SPI kanál pro komunikaci s převodníky a vynuluje hodnoty proměnných.

Ve smyčce hlavního programu jsou nastavovány hodnoty napětí a proudu na DA převodníků. Hodnoty z AD převodníku – napětí a proud, jsou ukládány do pole dat a jsou počítány průměrné hodnoty z toho, co je naměřeno.

Hlavičkový soubor definuje makra, deklaruje proměnné a funkce, které budou potřebné v hlavním programu

/*********************************************************************************
* File Name        : AT_slave_test.h
* Created        : 16.5.2016
* Autor            : Frantisek Pospisil
*
*********************************************************************************/


/* definice maker */
#define        LED1_set        PORTC    &=~0x20;        // cervena
#define        LED1_clr        PORTC    |= 0x20;
#define        LED2_set        PORTC    &=~0x40;        // zelena
#define        LED2_clr        PORTC    |= 0x40;
#define        LED3_set        PORTC    &=~0x80;        // zelena
#define        LED3_clr        PORTC    |= 0x80;
#define        Nabijeni_set    PORTA    |= 0x01;        // rele nabijeni
#define        Nabijeni_clr    PORTA    &=~0x01;        // rele nabijeni
#define        Rel_30V            PORTA    |= 0x02;        // rele vinuti 15V / 30V
#define        Rel_15V            PORTA    &=~0x02;        // rele vinuti 15V / 30V
#define        Rel_filtr_set    PORTA    |= 0x04;        // rele kapacita filtru
#define        Rel_filtr_clr    PORTA    &=~0x04;        // rele kapacita filtru
#define        Rel_proud_10x    PORTA    |= 0x08;        // rele mereni 100x / 10x
#define        Rel_proud_100x    PORTA    &=~0x08;        // rele mereni 100x / 10x
#define        DA_CLR_clr        PORTC    &=~0x04;
#define        DA_CLR_set        PORTC    |= 0x04;
#define        DA_LDAC_clr        PORTC    &=~0x02;
#define        DA_LDAC_set        PORTC    |= 0x02;
#define        DA_SYNC_clr        PORTC    &=~0x18;        // 4-ty bit - chyba procesoru Michal
#define        DA_SYNC_set        PORTC    |= 0x18;        // 4-ty bit - chyba procesoru Michal
#define        AD_CS_set        PORTB    &=~0x01;
#define        AD_CS_clr        PORTB    |= 0x01;

/* deklarace globalnich promennych */
static unsigned int            TimCounter;            // pocitadlo casovace
static unsigned int            ProgramCounter;        // pocitadlo programu, merenych vzorku
static unsigned char        SerialCounter;        // pocitadlo serioveho kanalu
static unsigned char        SerialArray[22];    // pole vysilanych dat
static unsigned char        SerialCRC;            // scita CRC hodnotu

static unsigned int            VoltageInAd;        // aktualni namerene napeti
static unsigned int            CurrentInAd;        // aktualni namereny proud
static unsigned long int    VoltageInAdd;        // soucet
static unsigned long int    CurrentInAdd;
static unsigned int            VoltageInAverage;    // vypocitany prumer
static unsigned int            CurrentInAverage;
static unsigned int            VoltageArray[130];    // pole namerenych dat pro vypocet prumeru
static unsigned int            CurrentArray[130];

static unsigned int            VoltageOutDa;        // napeti pro DA prevodnik
static unsigned int            CurrentOutDa;        // proud pro DA prevodnik
static unsigned int            TempReference;        // teplota reference napeti
static unsigned char        ReleStatus;            // predava stav rele
static unsigned char        CurrentStatus;        // zobrazuje zdroj proudu

/* uplne funkcni prototypy */
/*********************************************************************************
*
* Function Name : DAC8563_Send
* Description    : odesle data na DAC
*
*********************************************************************************/
void DAC8563_Send(unsigned char command, unsigned int val);

/*********************************************************************************
*
* Function Name : ADS8341_Send
* Description    : prijme data z AD prevodniku
*
*********************************************************************************/
unsigned int ADS8341_Send(unsigned char command);

/*********************************************************************************
*
* Function Name : SPI_Send
* Description    : odesle data na SPI
*
*********************************************************************************/
unsigned char SPI_Send(unsigned char val);

/*********************************************************************************
*
* Function Name : SystemInit
* Description    : inicializuje ridici registry procesoru a zapoji rele
*
*********************************************************************************/
void System_Init(void);

/*********************************************************************************
*
* Function Name : vypocet CRC ze zadaneho polynomu
* Description    : pole dat ze ktereho ma byt pocitano a velikost pole
* Return        : CRC kod
*
*********************************************************************************/
unsigned char CRC_Calculation( void );

Hlavní program

/*********************************************************************************
* File Name        : AT_slave_test.c
* Created        : 16.5.2016
* Autor            : Frantisek Pospisil
*
*********************************************************************************/

/* Hlavni program:
*        nastavuje DA prevodnik
*        cte data z AD prevodniku
*        pocita prumer z namerenych dat, pocet vzorku je 128, nebo 64, nebo 32
*        ovlada rele, podle promenne ReleStatus
*
* Preruseni serioveho kanalu:
*        prijimany i vysilany paket ma 20 znaku, kontroluje se CRC kod
*        prijima data z Master, detekuje znak 'Z' na zacatku, detekuje '0' na tretim miste
*        kdyz tyto znaky nejsou, nebude se vysilat
*        kdyz je vypocitane CRC shodne s prijatym na 20 znaku, pouzije data
*        vysila i kdyz je CRC prijateho paketu vadne
*/

/* hlavickove soubory */
#define CrcPolynom    0x91            // definice polynomu pro vypocet CRC
#define F_CPU 8000000UL
#include "main.h"
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define DaVoltage    0b00011001        // nastaveni DA prevodniku pro rizeni napeti
#define DaCurrent    0b00011000        // nastaveni DA prevodniku pro rizeni proudu
#define AdVoltage    0b11010110        // nastaveni AD prevodniku pro mereni napeti
#define AdCurrent    0b10010110        // nastaveni AD prevodniku pro mereni proudu
#define AdTempRef    0b11100110        // nastaveni AD prevodniku pro mereni teploty reference


// ===============================================================================
//                        HLAVNI PROGRAM
// ===============================================================================
int main(void)
{
    System_Init();            // inicializuje registry, promenne a rele pro nabijeni
    while(1)
    {
        DAC8563_Send( DaCurrent, CurrentOutDa );        // nastavi DA prevodnik
        DAC8563_Send( DaVoltage, VoltageOutDa );
        VoltageInAd = ADS8341_Send( AdVoltage );        // data z AD prevodniku
        CurrentInAd = ADS8341_Send( AdCurrent );
        TempReference = ADS8341_Send( AdTempRef );        // zmeri teplotu reference
        
        VoltageArray[ProgramCounter] = VoltageInAd;        // namerena data ulozi do pole dat
        CurrentArray[ProgramCounter] = CurrentInAd;
        VoltageInAdd += VoltageInAd;                    // namerena data pricte k prumeru
        CurrentInAdd += CurrentInAd;
        if( ProgramCounter == 128 )                        // pocet vzorku pro optimalizaci deleni 128, 64, 32
        {
            LED3_set;                                    // blika diodou
            ProgramCounter = 0;
        }
        else ProgramCounter++;
        if( ProgramCounter == 10 ) LED3_clr;
        VoltageInAdd -= VoltageArray[ProgramCounter];    // odecita posledni vzorek v poli
        CurrentInAdd -= CurrentArray[ProgramCounter];
        VoltageInAverage = ( VoltageInAdd >> 7 ) & 0xFFFF;    // deleni
        CurrentInAverage = ( CurrentInAdd >> 7 ) & 0xFFFF;

        if(( ReleStatus&0x01 ) && ( ~CurrentStatus&0x80 )) 
        {
            Nabijeni_clr;                                // 7 bit CurrentStatus indikuje zmenu napeti
            _delay_ms( 100 );
            Rel_30V;                                    // zapinani 30V vinuti 
            _delay_ms( 300 );
            Nabijeni_set;                                // nabijeni
            CurrentStatus |= 0x80;
        }
        if( ~ReleStatus&0x01 )                            // odpojeni 30V vinuti
        {
            Rel_15V;
            CurrentStatus &=~0x80;
        }
        if( ReleStatus&0x02 ) Rel_filtr_set                // resi stav rele na zaklade prijatych dat
        else Rel_filtr_clr;
        if( ReleStatus&0x04 ) Rel_proud_10x
        else Rel_proud_100x;
    }
}
/*********************************************************************************
*
* Function Name : TIMER1_CAPT_vect
* Description    : blika diodou
*
*********************************************************************************/
ISR(TIMER1_CAPT_vect)
{
    if ( TimCounter == 200 )    // pocita interval 0,2 sekunda
    {
        TimCounter = 0;
        LED1_set;
    }
    if ( TimCounter == 20 ) LED1_clr;    // zhasne dioda
    TimCounter++;
}
/*********************************************************************************
*
* Function Name : USART0_RXC_vect
* Description    : preruseni prijimace serioveho kanalu
*
*********************************************************************************/
ISR(USART_RXC_vect)
{
    LED2_set;
    SerialArray[SerialCounter] = UDR;        // prijata data do pole
    // kontroluje znak kanalu, kdyz neodpovida zarizeni, nastavi SerialCounter = 50
    if(( SerialCounter == 3 ) && ( SerialArray[3] != '0' )) SerialCounter = 50;
    if( SerialArray[0] == 'Z' ) SerialCounter++;    // na zacatku komunikace musi byt 'Z'
    if( SerialCounter == 66 ) SerialCounter = 0;    // konec komunikace, kdyz neodpovida zarizeni
    if( SerialCounter == 21 )                        // konec komunikace, kdyz odpovida zarizeni
    {
// ===============================================================================
//            kontrloa CRC, ulozeni prijatych dat a priprava na vysilani
// ===============================================================================        
        SerialCRC = CRC_Calculation();                // vypocita CRC kod
        if( SerialCRC == SerialArray[20] )            // CRC je v poradku, data budou pouzita v hlavnim programu
        {
            SerialArray[19] = 0;
            VoltageOutDa = (SerialArray[ 4] << 8) + SerialArray[ 5];    // z prijatych dat vypocita napeti DA prevodniku
            CurrentOutDa = (SerialArray[ 6] << 8) + SerialArray[ 7];    // z prijatych dat vypocita proud DA prevodniku
            ReleStatus = SerialArray[8];
        }
        else SerialArray[19] = 2;                                    // chyba CRC pri prijimani
        SerialArray[ 0] = 'Z';
        SerialArray[ 1] = 'D';
        SerialArray[ 2] = 'R';
        SerialArray[ 3] = '0';
        SerialArray[ 4] = (unsigned char) ( VoltageInAd >> 8 );        // horni polovina DA prevodniku napeti
        SerialArray[ 5] = (unsigned char) ( VoltageInAd );            // spodni polovina DA prevodniku napeti
        SerialArray[ 6] = (unsigned char) ( CurrentInAverage >> 8 );        // horni polovina DA prevodniku proudu
        SerialArray[ 7] = (unsigned char) ( CurrentInAverage );            // spodni polovina DA prevodniku proudu        
        SerialArray[ 8] = (unsigned char) ( VoltageInAverage >> 8 );
        SerialArray[ 9] = (unsigned char) ( VoltageInAverage );
        SerialArray[10] = (unsigned char) ( CurrentInAd >> 8 );
        SerialArray[11] = (unsigned char) ( CurrentInAd );
        SerialArray[12] = (unsigned char) ( TempReference >> 8 );        // teplota referencniho zdroje
        SerialArray[13] = (unsigned char) ( TempReference );            // teplota referencniho
        SerialArray[14] = 0;
        SerialArray[15] = 0;
        SerialArray[16] = 0;
        SerialArray[17] = 0;
        SerialArray[18] = 0;    
        SerialArray[20] = CRC_Calculation();    // posledni znak je CRC vysilanych dat
        while ( !( UCSRA & (1<<UDRE)));            // ceka na volny kanal    
        UDR = SerialArray[0];                    // posle prvni znak
        SerialCounter = 1;
    }
}
/*********************************************************************************
*
* Function Name : USART0_TXC_vect
* Description    : preruseni vysilace serioveho kanalu
*
*********************************************************************************/
ISR(USART_TXC_vect)
{
    if( SerialCounter < 21 )
    {
        UDR = SerialArray[SerialCounter];    // posle dalsi znak
        SerialCounter++;
    }
    else
    {
        LED2_clr;
        SerialCounter = 0;                    // konec vysilani
    }
}

/*********************************************************************************
*
* Function Name : DAC8563_Send
* Description    : odesle data na DAC
*
*********************************************************************************/
void DAC8563_Send(unsigned char command, unsigned int val)
{
    SPCR  = 0b01010100;        // nastaveni SPI kanalu
// zmena dat na vzestupnou hranu CLK, cteni na sestupnou hranu
//    LED3_set;
    DA_SYNC_clr;
    SPI_Send( command );    // cislo kanalu
    SPI_Send( val >> 8 );    // horni polovina
    SPI_Send( val );        // spodni polovina
    DA_SYNC_set;
//    LED3_clr;
}

/*********************************************************************************
*
* Function Name : ADS8341_Send
* Description    : prijme data z AD prevodniku
* Note            : PB1 - vstup BUSY
*
*********************************************************************************/
unsigned int ADS8341_Send(unsigned char command)
{
    unsigned int AdVal;
    
    SPCR  = 0b01011100;        // nastaveni SPI kanalu
//zmena dat na sestupnou hranu CLK, cteni na vzestupnou hranu
    AD_CS_set;
    SPI_Send( command );    // cislo kanalu
    while( PINB&0x02 );        // ceka na signal BUSY
    AD_CS_clr;
    while(~PINB&0x02 );
    AD_CS_set;
    AdVal =    SPI_Send( 0 ) << 8;            // horni polovina
    AdVal = AdVal + (SPI_Send( 0 ));    // spodni polovina
    SPI_Send( 0 );
    AdVal = AdVal - 32768;                // 0 je uprostred rozsahu, posune se o polovinu
    AD_CS_clr;
    return ( AdVal );
}

/*********************************************************************************
*
* Function Name : SPI_Send
* Description    : odesle data na SPI
*
*********************************************************************************/
unsigned char SPI_Send(unsigned char val)
{
    SPDR = val;                        // odesilana data
    while(!(SPSR & (1<<SPIF)));
    return SPDR;                    // prijata data
}

/*********************************************************************************
*
* Function Name : SystemInit
* Description    : inicializuje ridici registry procesoru a zapoji rele
*
*********************************************************************************/
void System_Init(void)
{
    PORTC = 0b00000000;        // H-pull up, L vysoká impedance
    DDRC  = 0b11111110;        // H-output, L-input - DA prevodnik
    PORTB = 0b00000000;        // H-pull up, L vysoká impedance
    DDRB  = 0b10110001;        // H-output, L-input - SPI sbernice
    PORTA = 0b00000000;
    DDRA  = 0b11111111;        // rele
    PORTD = 0b00000000;
    DDRD  = 0b00000010;        // seriovy kanal
    // * * * * * * * * * * * * * * * * * * * * * * * * * SPI sbernice
    SPCR  = 0b01010111;
    // preruseni SPI, aktivace SPI, prenos od MSB do LSB, Master=1
    // polarita hodinoveho signalu, faze hodin, prenosova rychlost
    SPSR  = 0b00000000;
    // * * * * * * * * * * * * * * * * * * * * * * * * * seriovy kanal
    UBRRH = 0;
    UBRRL = 16;                // rychlost prenosu 9600 baudu - 103
                            // rychlost prenosu 57600 baudu - 16
    UCSRA = 0b00000010;        // dvojnasobna rychlost
    UCSRB = 0b11011000;        // povoleni vysilace a prijimace, preruseni pri vysilani i prijimani
    UCSRC = 0b10001110;        // bez parity, osum bitu
    // * * * * * * * * * * * * * * * * * * * * * * * * * Nastaveni casovace T1
    TCCR1A = 0b00000000;    // Frekvence = F krystal
    TCCR1B = 0b00011010;    // rezim PWM 12, citac se vynuluje pri dosazeni hodnoty ICR1
    TIMSK  = 0b00100000;    // preruseni pri priznaku OCF1
    ICR1   = 1000;            // 1000 Hz kmitocet PWM

    // * * * * * * * * * * * * * * * * * * * * * * * * * Inicializace promennych
    TimCounter            = 0;    // pocitadlo casovace
    SerialCounter        = 0;    // vysilany, nebo prijimany znak na seriovem kanalu
    VoltageOutDa        = 0;    // data pro DA prevodnik
    CurrentOutDa        = 0;    // data pro DA prevodnik
    ProgramCounter        = 0;    // pocitadlo v hlavnim programu - vzorky mereni
    CurrentStatus        = 0;    // stav zdroj napeti / proudu Bit 0, prepinani vinuti Bit 7
    VoltageInAdd        = 0;    // vypocet prumerne hodnoty
    CurrentInAdd        = 0;    // vypocet prumerne hodnoty
        
    LED1_set;
    LED2_set;
    LED3_set;
// ===============================================================================
//                        inicializace DA prevodniku
// ===============================================================================
    DA_CLR_set;
    DA_LDAC_clr;
    DA_SYNC_set;
    AD_CS_clr;
    DAC8563_Send( 0b00101000, 1 ); // Reset
    DAC8563_Send( 0b00111000, 0 ); // Disable internal reference
    DAC8563_Send( 0b00100000, 3 ); // power up DAC-A and DAC-B
    DAC8563_Send( 0b00000010, 0 ); // Gain = 2
    DAC8563_Send( 0b00110000, 3 ); // LDAC pin inaktive for DAC-A and DAC-B
    DAC8563_Send( DaVoltage, 0 );
    DAC8563_Send( DaCurrent, 0);
    _delay_ms( 1500 );                // ceka na nabiti
    LED1_clr;
    _delay_ms( 500 );
    LED2_clr;
    _delay_ms( 500 );
    LED3_clr;
    _delay_ms( 500 );
    Nabijeni_set;                    // pripoji rele nabijeni
    sei();                            // pusti globalni preruseni
}

/*********************************************************************************
*
* Function Name : vypocet CRC podle polynomu
* Return        : CRC kod
*
*********************************************************************************/
unsigned char CRC_Calculation( void )
{
    unsigned char i, j, CrcCode = 0;
    
    for( i = 0; i < 20; i++ )        // pole dat ma 20 znaku
    {
        CrcCode ^= SerialArray[i];    // XOR CRC kodu a dat
        for( j = 0; j < 8; j++)
        {
            if( CrcCode & 1 )
            CrcCode ^= CrcPolynom;    // XOR CRC kodu a poynomu, kdyz je bit0 = 1
            CrcCode >>=1;            // rotace CRC kodu
        }
    }
    return CrcCode;
}

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna.

Čeština‎EnglishFrançaisDeutschItalianoPolskiРусскийEspañol