quarta-feira, 15 de junho de 2011

Comunicação SPI entre microcontroladores AVR:

Para este exemplo vou usar 2 microcontroladores ATmega8 comunicando entre si por meio do protocolo SPI. Usando o  AVRstudio(gratuito) . 
O circuito no simulador proteus :




Configurando os fuses:




O código fonte do slave:



 1:  //************************************************************************
   2:  //                             usando SPI
   3:  //  Version    : 1.0
   4:  //  microcontrolador : AVR ATMega8
   5:  //  Autor        : Aguivone
   6:  //  descrição  : modo slave - coloca dado recebido na saida do portd  
   7:  //  data : 14/06/2011.
   8:  //
   9:  ///////////////////////////////////////////////////////////////////////////////////////////////
  10:   
  11:  #define F_CPU 4000000UL           // clock do microcontrolador
  12:  #include <avr/io.h>
  13:  #include <avr/interrupt.h>
  14:  /////////////////////////////////configura registradores////////////////// 
  15:  #define MOSI     PINB3//entrada
  16:  #define MISO     PINB4//saida
  17:  #define SCK      PINB5//entrada
  18:  #define DDR_SPI  DDRB
  19:   
  20:  char enviar=0;
  21:   
  22:  ////////////////////////////////////////////////////////////// 
  23:  void SPI_Slave_inicializa(void)
  24:  {
  25:      DDR_SPI = (1<<MISO);
  26:      SPCR = (1<<SPE)/*|(1<<CPOL)*/|(1<<SPIE);      // habilita SPI / polaridade do clock / habilita interrupção de SPI 
  27:  }
  28:  ///////////////vetor de interrupção///////////////////////////////////
  29:  ISR (SPI_STC_vect)                         
  30:  {
  31:    SPDR = enviar;//envia dado anteriormente recebido
  32:    PORTD = SPDR;
  33:    enviar = SPDR;
  34:  }
  35:  /////////////////////////////////////função principal///////////////////////////////// 
  36:  int main(void) 
  37:  { 
  38:      SPI_Slave_inicializa();
  39:      DDRD = 0xFF;
  40:      sei();
  41:   
  42:          for (;;)
  43:          {
  44:           //faz nada 
  45:          }
  46:  }
  47:   

O código fonte do mestre:

   1:  //************************************************************************************************
   2:  //                             usando o protocolo SPI
   3:  //  Version    : 1.0
   4:  //  microcontrolador : AVR ATMega8
   5:  //  Autor        : Aguivone 
   6:  //  descrição  : envia dados e lê ao mesmo tempo
   7:  //  data : 14/06/11.
   8:  //
   9:  //para simular este exemplo basta ligar a saida serial com a entrada assim fica facil percerber que ele envia 
  10:  //e recebe ao mesmo tempo
  11:  //*************************************************************************************************
  12:   
  13:  #define F_CPU 4000000UL           // clock do microcontrolador
  14:  #include <avr\io.h>
  15:  #include <util/delay.h>
  16:  #include <avr/interrupt.h>
  17:   
  18:  /////////////////////////////configuração dos pinos utilizados //////////////////////////////////////
  19:   
  20:  #define MOSI         PB3
  21:  #define MISO         PB4
  22:  #define SCK          PB5
  23:  #define SS           PB2
  24:   
  25:  /////////////////////////////////////////////////////////////////////////////////////////////////////////
  26:  void SPI_inicializa(void)
  27:  {
  28:      DDRD =0xFF;
  29:      DDRB |= ((1<<MOSI)|(1<<SCK)|(1<<SS));    //MOSI, SCK and SS são saidas(se for usar dois mestres então deve-se o SS como entrada) 
  30:      DDRB &= (~(1<<MISO));                    //MISO é entrada
  31:      PORTB |= (1<<SS);                        //inicia com SS em nivel alto
  32:      //SPE : habilita SPI
  33:      //MSTR: modo  Master 
  34:      //SPIE:  habilita interrupção de SPI
  35:      //SPCR = ((1<<SPE)|(1<<MSTR)|(1<SPR1)|(1<SPR0));    //(1<SPR1)|(1<SPR0) : FOSC/128
  36:      SPCR = ((1<<SPE)|(1<<MSTR)|(1<SPR0));    //(1<SPR0) : FOSC/16
  37:  }
  38:   
  39:  void SPI_envia_byte(char dados)
  40:  {
  41:      PORTB &= (~(1<<SS));    //coloca SS em nivel baixo (0), para transferir dados
  42:      SPDR = dados;        //inicializa transferencia
  43:      while(!(SPSR & (1<<SPIF)));    //espera fim de transmissão
  44:      PORTD = SPDR;//escreve no port D o dado recebido
  45:      PORTB |= (1<<SS);    //coloca SS em nivel alto (1), pois é o fim da transmissão.
  46:      _delay_ms(1);//tempo para slave colocar dados no registrador
  47:  }
  48:   
  49:  int main(void) 
  50:  {
  51:    
  52:    SPI_inicializa();//inicializa SPI
  53:    sei();//habilita interrupções
  54:   
  55:    while(1)
  56:     {
  57:      SPI_envia_byte(0X01);//envia primeiro dado
  58:      SPI_envia_byte(0X05);//envia segundo dado
  59:      SPI_envia_byte(0X09);//envia terceiro dado
  60:      SPI_envia_byte(0X30);//envia quarto dado
  61:      SPI_envia_byte(0X60);//envia quinto dado
  62:      }
  63:   
  64:    return 0;
  65:  }






Usando comunicação I2C no AVR (memoria 24CXX) :

O circuito no simulador proteus:


configurando os fuses:

 
O código fonte :

   1:  //************************************************************************

   2:  //                             usando o protocolo I2C (

   3:  //  Version    : 1.0

   4:  //  microcontrolador : AVR ATMega8
   5:  //  Autor        : Aguivone (fiz só uma adaptação )
   6:  //  descrição  : embora ele seja chamado de two wire interface(TWI) só difere do I2C
   7:  //  pelo numero de endereços (dispositivos) na linha de comunicação.
   8:  //  data : 08/06/11.
   9:  //
  10:  //************************************************************************
  11:  #include <inttypes.h>
  12:  #include <avr/io.h>
  13:  #include <util/twi.h>        /* header que contem algumas macros utilizadas */
  14:   
  15:  #define F_CPU 4000000L    /*frequencia em hz , valor do cristal*/
  16:   
  17:  /*
  18:   * I2C endereços nas 24Cxx EEPROM:
  19:   * 
  20:   * 1 0 1 0 E2 E1 E0 R/~W    24C01/24C02
  21:   * 1 0 1 0 E2 E1 A8 R/~W    24C04
  22:   * 1 0 1 0 E2 A9 A8 R/~W    24C08
  23:   * 1 0 1 0 A10 A9 A8 R/~W    24C16
  24:    assim para escrever por exemplo na memoria 24C16 deve se colocar 1010 + o endereço do banco de memoria que vai acessar +sinaliza se é escrita ou leitura
  25:  
  26:   */
  27:   
  28:  #define ENDE_ESCRITA    0xA0    /* E2 E1 E0 = 0 0 0 banco zero */
  29:  #define PAGE_SIZE 8 // no maximo 8 bytes por linha podem ser escrito(1pagina = 8bytes)
  30:   
  31:  uint8_t twst;
  32:   
  33:  void I2C_inicializar(void)//configura comunicação
  34:  {
  35:            /* 100 kHz clock do barramento I2C, TWPS = 0 => prescaler = 1 */
  36:          #if defined(TWPS0)/* has prescaler (mega128 & newer) */
  37:            {
  38:            TWSR = 0;
  39:            }
  40:          #endif
  41:          #if F_CPU < 3600000UL
  42:            TWBR = 10;            /* menor valor TWBR */
  43:          #else
  44:            TWBR = (F_CPU / 100000UL - 16) / 2; //se a frequencia do clock for maior que 3,6Mhz
  45:          #endif
  46:  }
  47:   
  48:   
  49:  int ler_bytes(uint16_t eeaddr, int len, uint8_t *buf)
  50:  {
  51:    uint8_t sla, twcr, n = 0;
  52:    int rv = 0;
  53:   
  54:    /* busca o endereço de memoria e seleciona qual banco de memoria vai usar */
  55:    sla = ENDE_ESCRITA | (((eeaddr >> 8) & 0x07) << 1);
  56:    restart:
  57:            if (n++ >= 200)//numero maximo de tentativas
  58:              return -1;
  59:    begin:
  60:   
  61:            TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* inicio de transmissao*/
  62:            while ((TWCR & _BV(TWINT)) == 0) ; /* espera interrupção */
  63:            switch ((twst = TW_STATUS))
  64:              {
  65:              case TW_REP_START:        /* OK,pula para proxima etapa*/
  66:              case TW_START:
  67:                break;
  68:   
  69:              case TW_MT_ARB_LOST:    /* informação perdida*/
  70:                goto begin;
  71:   
  72:              default:
  73:                return -1;        /* retorna erro */
  74:                  
  75:              }
  76:   
  77:            /* endeço mais bit de escrita SLA+W */
  78:            TWDR = sla | TW_WRITE;
  79:            TWCR = _BV(TWINT) | _BV(TWEN); /* inicio de transmissao*/
  80:            while ((TWCR & _BV(TWINT)) == 0) ; /* espera interrupção */
  81:            switch ((twst = TW_STATUS))
  82:              {
  83:              case TW_MT_SLA_ACK:
  84:                break;
  85:   
  86:              case TW_MT_SLA_NACK:    /*sem resposta manda reiniciar*/
  87:                goto restart;
  88:   
  89:              case TW_MT_ARB_LOST:    /* re-arbitrate */
  90:                goto begin;
  91:   
  92:              default:
  93:                goto error;        
  94:              }
  95:   
  96:            TWDR = eeaddr;                     /* coloca endereço*/
  97:            TWCR = _BV(TWINT) | _BV(TWEN);     /* limpa interrupção  */
  98:            while ((TWCR & _BV(TWINT)) == 0) ; /* espera para transmitir */
  99:            switch ((twst = TW_STATUS))
 100:              {
 101:              case TW_MT_DATA_ACK:
 102:                break;
 103:   
 104:              case TW_MT_DATA_NACK://se não receber dados aborta transmissao
 105:                goto quit;
 106:   
 107:              case TW_MT_ARB_LOST:
 108:                goto begin;
 109:   
 110:              default:
 111:                goto error;        
 112:              }
 113:   
 114:            /*
 115:             * modo master recebe
 116:             */
 117:            TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /*limpa interrupções*/
 118:            while ((TWCR & _BV(TWINT)) == 0) ; /* espera para inicia transmissaõ*/
 119:            switch ((twst = TW_STATUS))
 120:              {
 121:              case TW_START:        /* OK,vai para proxima etapa*/
 122:              case TW_REP_START:
 123:                break;
 124:   
 125:              case TW_MT_ARB_LOST:
 126:                goto begin;
 127:   
 128:              default:
 129:                goto error;
 130:              }
 131:   
 132:            /* send SLA+R */
 133:            TWDR = sla | TW_READ;
 134:            TWCR = _BV(TWINT) | _BV(TWEN); /* limpa interrupções */
 135:            while ((TWCR & _BV(TWINT)) == 0) ; /*espera para enviar dados */
 136:            switch ((twst = TW_STATUS))
 137:              {
 138:              case TW_MR_SLA_ACK:
 139:                break;
 140:   
 141:              case TW_MR_SLA_NACK:
 142:                goto quit;
 143:   
 144:              case TW_MR_ARB_LOST:
 145:                goto begin;
 146:   
 147:              default:
 148:                goto error;
 149:              }
 150:   
 151:            for (twcr = _BV(TWINT) | _BV(TWEN) | _BV(TWEA) /* Note [13] */;
 152:                 len > 0;
 153:                 len--)
 154:              {
 155:                if (len == 1)
 156:              twcr = _BV(TWINT) | _BV(TWEN); /* envia NACK*/
 157:                TWCR = twcr;        /* limpa interrupções */
 158:                while ((TWCR & _BV(TWINT)) == 0) ; /* espera para enviar dados */
 159:                switch ((twst = TW_STATUS))
 160:              {
 161:              case TW_MR_DATA_NACK:
 162:                len = 0;        /* fim do loop houve falha na resposta*/
 163:              case TW_MR_DATA_ACK:
 164:                *buf++ = TWDR;  //pega dados da memoria
 165:                rv++;
 166:                break;
 167:   
 168:              default:
 169:                goto error;
 170:              }
 171:              }
 172:    quit:
 173:   
 174:            TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); /* envia stop bit */
 175:   
 176:    return rv;
 177:            error:
 178:            rv = -1;
 179:            goto quit;
 180:  }
 181:   
 182:  ///////////////////////////////////função que escreve 1 pagina(8 bytes)///////////////
 183:   
 184:  int escreve_pagina(uint16_t endereco, int taman, char *dados)
 185:  {
 186:    uint8_t sla, n = 0;
 187:    int rv = 0;
 188:    uint16_t fim_ende;
 189:   
 190:    if (endereco + taman < (endereco | (PAGE_SIZE - 1)))//verifica se cabe na pagina atual
 191:      fim_ende = endereco + taman;
 192:    else
 193:      fim_ende = (endereco | (PAGE_SIZE - 1)) + 1;
 194:      taman = fim_ende - endereco;
 195:   
 196:    /* pega os bits altos da memoria */
 197:    sla = ENDE_ESCRITA  | (((endereco >> 8) & 0x07) << 1);
 198:   
 199:    restart://rotina de reinicio
 200:                if (n++ >= 200)//numero de interações
 201:                  return -1;
 202:    begin:
 203:                TWCR = _BV(TWINT) | _BV(TWSTA) | _BV(TWEN); /* limpa interrupções */
 204:                while ((TWCR & _BV(TWINT)) == 0) ; /* espera pra enviar*/
 205:                switch ((twst = TW_STATUS))
 206:                  {
 207:                  case TW_REP_START:        /* OK, vai para proxima etapa */
 208:                  case TW_START:
 209:                    break;
 210:   
 211:                  case TW_MT_ARB_LOST:
 212:                    goto begin;
 213:   
 214:                  default:
 215:                    return -1;        /* inicio de transmissão falhou*/
 216:                              /* não fará uma condição de finalização*/
 217:                  }
 218:   
 219:                /* send SLA+W */
 220:                TWDR = sla | TW_WRITE;//coloca os 8 bits alto do endereço
 221:                TWCR = _BV(TWINT) | _BV(TWEN); /* limpa interrupções para iniciar uma transmissão*/
 222:                while ((TWCR & _BV(TWINT)) == 0) ; /* espera terminar transmissão */
 223:                switch ((twst = TW_STATUS))
 224:                  {
 225:                          case TW_MT_SLA_ACK:
 226:                            break;
 227:   
 228:                          case TW_MT_SLA_NACK:    /* ainda está escrevendo reinicia*/
 229:                            goto restart;
 230:   
 231:                          case TW_MT_ARB_LOST:    /* tentar novamente*/
 232:                            goto begin;
 233:   
 234:                          default:
 235:                            goto error;        /* pede fim de transmissão */
 236:                  }
 237:   
 238:                TWDR = endereco;        /*copia os 8 bits baixo do endereço*/
 239:                TWCR = _BV(TWINT) | _BV(TWEN); /* limpa interrupções para iniciar uma transmissão*/
 240:                while ((TWCR & _BV(TWINT)) == 0) ; /* espera terminar transmissão */
 241:                switch ((twst = TW_STATUS))
 242:                  {
 243:                  case TW_MT_DATA_ACK:
 244:                    break;
 245:   
 246:                  case TW_MT_DATA_NACK:
 247:                    goto quit;
 248:   
 249:                  case TW_MT_ARB_LOST:
 250:                    goto begin;
 251:   
 252:                  default:
 253:                    goto error;        /* pede fim de transmissão */
 254:                  }
 255:   
 256:                    for (; taman > 0; taman--)
 257:                      {
 258:                         TWDR = *dados++;
 259:                         TWCR = _BV(TWINT) | _BV(TWEN); /* limpa interrupções para iniciar uma transmissão*/
 260:                         while ((TWCR & _BV(TWINT)) == 0) ; /* espera terminar transmissão */
 261:                         switch ((twst = TW_STATUS))
 262:                          {
 263:                          case TW_MT_DATA_NACK:
 264:                            goto error;        /* indica que local de memoria é protegido */
 265:   
 266:                          case TW_MT_DATA_ACK:
 267:                            rv++;
 268:                            break;
 269:   
 270:                          default:
 271:                            goto error;
 272:                          }
 273:                      }
 274:    quit: ///rotina para sair 
 275:                    TWCR = _BV(TWINT) | _BV(TWSTO) | _BV(TWEN); /* finaliza transmissão*/
 276:   
 277:                    return rv;//retorna o numero de bytes escrito
 278:   
 279:    error:
 280:                    rv = -1;
 281:                    goto quit;
 282:  }
 283:  ////////////////////////////escreve uma frase //////////////////////////////////////////////////////////////////////////
 284:  ///////repete escrita até que todos os caracteres sejam escritos ou a função retorne um erro
 285:  int escreve_frase(uint16_t ende, int tamanho, char *frase)//endereço inicial ,quantidade de caracteres , string(frase)
 286:  {
 287:            int ret_valor, total=0;
 288:   
 289:            do
 290:              {
 291:                      ret_valor = escreve_pagina(ende , tamanho, frase);
 292:                      if (ret_valor == -1)
 293:                           {
 294:                            return -1;///se erro retorna -1
 295:                           }
 296:                        ende += ret_valor;
 297:                        tamanho -= ret_valor;
 298:                        frase += ret_valor;
 299:                        total += ret_valor;
 300:              }while (tamanho > 0);
 301:   
 302:            return total;//retorna a quantidade de caracteres escritos
 303:  }
 304:   
 305:   
 306:  int main(void)
 307:  {
 308:        int valor_retornado;
 309:        uint8_t frase_lida[16];
 310:        DDRD = 0XFF;
 311:        PORTD = 0X00;
 312:   
 313:        I2C_inicializar();
 314:         valor_retornado = escreve_frase(450,50, "TESTANDO ENVIO DE DADOS");// local a ser escrito(endereço inicial) /tamanho da frase(numero de caracteres/ frase (string)
 315:        valor_retornado = escreve_frase(23,1,"X");//escreve um caracter
 316:            if(valor_retornado<0)
 317:          {
 318:           //aqui deve colocar o que fazer se tiver uma falha de escrita
 319:          }
 320:          else
 321:          {
 322:           //aqui deve colocar o que fazer se não tiver nenhuma falha de escrita
 323:   
 324:          }  
 325:         //para ler bytes da memoria faça
 326:          valor_retornado = ler_bytes (1, 15, frase_lida);//local inicial de leitura / quantidade de caracteres a ler / bytes lido
 327:          if(valor_retornado<0)
 328:          {
 329:           //aqui deve colocar o que fazer se tiver uma falha de leitura
 330:           PORTD = 0X0F;
 331:          }
 332:          else
 333:          {
 334:           //aqui deve colocar o que fazer se não tiver nenhuma falha de leitura
 335:           PORTD = 0X05;
 336:   
 337:          } 
 338:           
 339:  }