Outils pour utilisateurs

Outils du site


robotics:computing:communication_twi_entre_atmega

Ceci est une ancienne révision du document !


Introduction

Nous allons faire communiquer 2 atmega48 de chez ATMEL via la communication twi (i2c). Dans un premier temps, nous allons détailler un cas simple : un atmega en mode master et l'autre en mode slave.

Schema

Protocole

Ecriture sur l'esclave

La communication est initiée par le maitre qui envoie d'un bit de START. Ensuite, il envoie l'adresse de l'esclave sur 7 bits. Le 8ème bit sert à indiquer la direction l'échange (écriture ou lecture). L'esclave retourne un bit d'acquittement (Ce bit n'est pas obligatoire pour continuer la communication). Le maitre envoie sa donnée sur 8 bits (plusieurs octets peuvent être envoyé à la suite). L'esclave retourne un bit d'acquittement à chaque octet reçu. Cela signifie que l'esclave est prêt à recevoir un nouvel octet. Le maitre termine la communication est envoyant un bit de STOP et libère le bus. Un bit REPEATED STAR peut êter envoyé à la place

Lecture sur l'esclave

Dans le cas d'une lecture d'une donnée sur un esclave, la communication est initiée de la même manière que pour l'écriture : le maitre envoie le bit de START et l'adresse de l'esclave. Ensuite c'est l'esclave qui envoie les données. Le maitre retourne un bit d'acquittement pour signifier qu'il attend encore un octet (pas de bit d'acquittement pour la fin de transmission). Le maitre termine la communication comme pour l'écriture : envoi du bit de STOP.

Reprise du bus

Un bit REPEATED START peut être utilisé à la place du “STOP - START” quand le maitre veut par exemple adresser un autre esclave sans relacher le bus (pas de risque qu'un autre maitre ne lui prenne la parole).

Envoi géneral

Le maitre peut contacter tous les esclaves en une fois en utilisant une adresse générale (0x00). Chaque esclave est configuré pour répondre ou bien ignorer cette requête générale.

Fréquence d'horloge

C'est le maitre qui impose la fréquence d'horloge SCL. Généralement, on trouve des fréquences de 100KHz ou bien 400KHz

Implémentation

Registres TWI

TWCR registre de controle

Il contient tous les bits de controle de la communication :

  • TWINT : c'est le flag d'interruption
  • TWEA : ce bit permet d'activer l'envoie des bits d'acquittement.
  • TWSTA : Ce bit permet d'envoyer le bit de START
  • TWSTO : Ce bit permet d'envoyer le bit de STOP
  • TWWC : c'est le bit de collision
  • TWEN : Ce bit permet d'activer la communication TWI
  • TWIE : Ce bit permet d'activer les interruptions TWI.
TWAR registre d'adresse

Il contient l'adresse esclave

  • TWA6-TWA0 : Ces bits contiennent l'adresse esclave.
  • TWGCE : Ce bit indique si l'esclave répond ou ignore la requête générale émise par un maitre.
TWDR registre de données

Il contient l'octet reçu ou envoyé.

TWSR registre de status
  • TWS7-TWS3 : contient le code qui détermine l'état dans lequel se trouve la communication (adresse reçue ou donnée envoyée).
  • TWPS1-TWPS0 : contient le prescaler qui détermine la fréquence d'horloge SCL.
TWBR

C'est le registre qui contient la valeur de la fréquence d'horloge SCL. SCLfreq = CPUfreq/(16+(2*TWBR*Prescaler))

Exemple : On travaille avec un ATMEGA cadencé à 16MHz et on souhaite une vitesse de 100Kz. TWBR*Prescaler = (CPUfreq - 16 * SCLfreq)/(2*SCLfreq) = (16MHz - 16 * 100KHz) / (2 * 100 KHz) = 72 Si on prend un prescaler de 1, on a TWBR = 72.

Slave

L'implémentation de la communication twi sur l'esclave va utiliser les interruptions. A chaque fois que l'esclave va recevoir un octet, la routine d'interruption va être appelée. Dans cette routine, nous allons tester le registre TWSR qui va nous renseigner sur le type d'information qu'il vient de recevoir et pouvoir la traiter correctement.

/*
 * twi_slave.c
 *
 *  Created on: 26 févr. 2010
 *      Author: ldo
 */
 
volatile uint8_t twi_flag_read_complete = 1;
volatile uint8_t twi_flag_write_complete = 1;
 
volatile uint8_t *twi_receive_buffer;
volatile uint8_t *twi_transmit_buffer;
volatile uint8_t twi_i = 0;
 
uint8_t twi_nb_data_to_transmit;
 
ISR(TWI_vect)
{
  uint8_t status;
 
  status = TWSR & 0xF8;
  switch (status)
    {
    /* Slave Receiver */
  case (TW_SR_SLA_ACK):/* 0x60 : SLA+W received, ACK returned */
    break;
  case (TW_SR_ARB_LOST_SLA_ACK):/*0x68 : arbitration lost in SLA+RW, SLA+W received, ACK returned */
    break;
  case (TW_SR_GCALL_ACK): /*0x70 : general call received, ACK returned */
    break;
  case (TW_SR_ARB_LOST_GCALL_ACK): /* 0x78 : arbitration lost in SLA+RW, general call received, ACK returned */
    break;
  case (TW_SR_DATA_ACK): /* 0x80 : data received, ACK returned */
    twi_receive_buffer[0] = TWDR;
    break;
  case (TW_SR_DATA_NACK): /* 0x88 : data received, NACK returned */
    break;
  case (TW_SR_GCALL_DATA_ACK): /* 0x90 : general call data received, ACK returned */
    break;
  case (TW_SR_GCALL_DATA_NACK):/* 0x98 : general call data received, NACK returned */
    break;
  case (TW_SR_STOP):/* 0xA0 : stop or repeated start condition received while selected */
    twi_flag_write_complete = 1;
    break;
 
    /* Slave Transmitter */
  case (TW_ST_SLA_ACK): /* 0xA8 : SLA+R received, ACK returned*/
    twi_i = 0;
    TWDR = twi_transmit_buffer[twi_i];
    twi_i++;
    break;
  case (TW_ST_ARB_LOST_SLA_ACK): /* 0xB0 : arbitration lost in SLA+RW, SLA+R received, ACK returned */
    break;
  case (TW_ST_DATA_ACK): /* 0xB8 : data transmitted, ACK received */
    TWDR = twi_transmit_buffer[twi_i];
    twi_i++;
    break;
  case (TW_ST_DATA_NACK):/*0xC0 : data transmitted, NACK received */
    break;
  case (TW_ST_LAST_DATA): /* 0xC8 : last data byte transmitted, ACK received */
    break;
 
  default:
    break;
    }
 
  TWCR |= (1 << TWINT); // TWINT flag bit is cleared
}
 
/* TWI slave setup */
void
twi_slave_setup(uint8_t address, volatile uint8_t * buffer,
    volatile uint8_t *receive_buffer)
{
  TWAR = (address << 1); // Set own TWI slave address. Accept TWI General Calls.
  twi_transmit_buffer = buffer;
  twi_receive_buffer = receive_buffer;
  TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE);
  SREG |= (1 << SREG_I);
}
 
volatile uint8_t tb[3], rb[3] ;
 
void
setup(void)
{
  DDRD = 0xFF;
  DDRC = 0x00;
  twi_slave_setup(0x68, tb, rb);
}
 
int
main(void)
{
  setup();
 
  while (1)
    {
      if (rb[0]==0x41)
      {
        tb[0] = 0x47;
        tb[1] = 0x49;
      }
      else
      {
        tb[0] = 0x27;
        tb[1] = 0x29;
      }
   }
}

Master

Ce code permet d'envoyer une donnée à un esclave.

Dans un premier temps, on va définir la vitesse de communication en initialisant TWBR et le prescaler dans twiMasterInitialise().

/*
 * twiMaster.c
 *
 *  Created on: 25 nov. 2009
 *      Author: ldo
 */
 
volatile uint8_t slave_address;
 
volatile uint8_t twi_flag_read_complete = 1;
volatile uint8_t twi_flag_write_complete = 1;
 
volatile uint8_t *twi_receive_buffer;
volatile uint8_t *twi_transmit_buffer;
volatile uint8_t twi_i = 0;
 
uint8_t twi_mode;
uint8_t twi_nb_data_to_transmit, twi_nb_data_to_receive;
 
/*
 * SLAVE:
 * receive only 1 data (register) */ISR(TWI_vect)
{
  uint8_t status;
 
  status = TWSR & 0xF8;
  switch (status)
    {
  case (TW_START): /* 0x08 : start condition transmitted */
  case (TW_REP_START): /* 0x10 : repeated START condition transmitted */
    twi_address();
    twi_i = 0;
    break;
  case (TW_MT_ARB_LOST): /* 0x38 : arbitration lost in SLA+RW, SLA+R received, ACK returned */
    break;
 
    /* Master Transmitter */
  case (TW_MT_SLA_ACK): /* 0x18 : SLA+W transmitted; ACK received */
  case (TW_MT_SLA_NACK): /* 0x20 : SLA+W transmitted; NACK received */
    TWDR = twi_transmit_buffer[0];
    TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE) | (1 << TWINT);
    twi_i++;
    break;
 
  case (TW_MT_DATA_ACK):/* 0x28 : data has been transmitted; ACK has been received */
  case (TW_MT_DATA_NACK): /* 0x30 : data has been transmitted; NACK has been received */
    if (twi_i == twi_nb_data_to_transmit)
      {
        if (twi_mode == WRITE)
          {
            twi_stop();
            twi_flag_write_complete = 1;
          }
        else // READ
          {
            slave_address++;
            twi_start();
          }
      }
    else
      {
        twi_data(twi_transmit_buffer[twi_i]);
        twi_i++;
      }
    break;
 
    /* Master Receiver */
  case (TW_MR_SLA_ACK):/* 0x40 : SLA+R has been transmitted; ACK has been received */
 
  case (TW_MR_SLA_NACK): /* 0x48 : SLA+R has been transmitted; NACK has been received */
    if (twi_nb_data_to_receive == 1)
      {
        TWCR &= ~(1 << TWEA); /* desactiver ACK pour la derniere demande */
      }
    break;
  case (TW_MR_DATA_ACK):/* 0x50 : data received, ACK transmitted */
  case (TW_MR_DATA_NACK): /* 0x58 : data received, NACK transmitted */
    twi_receive_buffer[twi_i] = TWDR;
    twi_i++;
 
    if (twi_i == (twi_nb_data_to_receive - 1))
      {
        TWCR &= ~(1 << TWEA); /* desactiver ACK pour la derniere demande */
      }
    if (twi_i == twi_nb_data_to_receive)
      {
        twi_flag_read_complete = 1;
        twi_stop();
      }
    break;
 
  default:
    break;
    }
 
  TWCR |= (1 << TWINT); // TWINT flag bit is cleared
}
 
/*
 * twi master setup
 * SCLfreq = CPUfreq / (16 + 2 * TWBR * presc)
 * */
void
twi_master_setup(void)
{
  PORTC = (1 << PC5) | (1 << PC4); // activate internal pull_ups for twi
  TWSR = 0x00;    // no prescaler
  TWBR = 72; // 100kHz @16Mhz
  //TWBR = 0x0C;    // 400 KHz @16MHz
  TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE) | (1 << TWINT);
  SREG |= (1 << SREG_I);
}
 
void
TWI_wait(void)
{
  while (!(TWCR & (1 << TWINT)))
    ;
}
 
/* transmit START condition */
void
twi_start(void)
{
  TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWIE) | (1 << TWINT) | (1 << TWSTA); /* send start condition */
}
 
/* transmit slave address SLA */
void
twi_address(void)
{
  TWDR = slave_address; /* load SLA into TWDR */
 
  TWCR &= ~(1 << TWSTA); /* desactivate START */
}
 
/* transmit data */
void
twi_data(uint8_t data)
{
  TWDR = data;
}
 
/* transmit STOP condition */
void
twi_stop(void)
{
  TWCR |= (1 << TWSTO); /* STOP */
}
 
void
twi_write_bytes(uint8_t add, uint8_t nb_bytes, volatile uint8_t *buffer)
{
  if (twi_flag_write_complete == 1)
    {
      twi_flag_write_complete = 0;
      slave_address = add << 1;
      twi_transmit_buffer = buffer;
      twi_start();
    }
}
 
void
twi_read_bytes(uint8_t add, volatile uint8_t *reg, uint8_t nb_bytes,
    volatile uint8_t *buffer)
{
  if (twi_flag_read_complete == 1)
    {
      twi_flag_read_complete = 0;
      twi_flag_write_complete = 0;
 
      slave_address = add;
 
      twi_transmit_buffer = reg;
 
      twi_mode = READ;
      twi_nb_data_to_transmit = 1;
      twi_nb_data_to_receive = nb_bytes;
 
      twi_receive_buffer = buffer;
 
      twi_start();
      while (twi_flag_read_complete == 0)
        ;
    }
}
robotics/computing/communication_twi_entre_atmega.1380290068.txt.gz · Dernière modification: 2013/09/27 15:54 par ldo