martes, 7 de febrero de 2017

PRACTICA # 20 CAN BUS (Controller Area Network)

OBJETIVO:
       Haremos uso del módulo CAN del MCU R5F563NB de la tarjeta de evaluación YRDKRX63N. Crearemos 3 nodos para demostrar la comunicación bidireccional entre ambos. Dos nodos serán con mcu pic18f2680. El primero de ellos (enviar) solo enviará un frame de datos de 8 bits el cual el primer byte será el valor analógico de un potenciómetro los demás datos serán fijos. El segundo de ellos (recibir) se encargará de imprimir en el LCD el ID, tamaño de datos y los primeros 5 bytes. Así mismo enviara vía uart a una hypeterminal toda la información de la red CAN. La tarjeta YRDKRX63N será un nodo de envío y recepción de frame de datos. Imprimiendo en la LCD lo del nodo (enviar) y también inyectara a la red CAN un frame de datos donde el primer byte es un incrementador y los demás serán bytes fijos.
  • Configuraremos stack de la API CAN BUS
  • Imprimiremos en el LCD el ID y valores del nodo que envía frames a la red CAN

DESARROLLO:
  • Del documento Renesas RX63N RDK User's Manual ubicamos el conector J6 del puerto CAN de la tarjeta.
  • Del YRDKRX63N schematic identificamos el circuito del CAN bus y su transceiver:

PASOS:
  • Creación de un proyecto:
1.- Abrir el software e2studio
2.- New/ C Project / Renesas RXC ToolChain


3.- Seleccionar el target R5F563NB, debug hardware Segger jLink, después next


4.- Seleccionar C/C++ Source file y por ultimo Finish.


5.- Para poder implementar correctamente el protocolo CAN y demostrar su funcionamiento es necesario primeramente crear una red de nodos con microcontroladores.
La red de nodos propuesta se muestra en la siguiente imagen:

NODO A “RECIBIR”:

NODO B “ENVIAR”:


El nodo B tiene un identificador ID = 24 y enviara 8 Bytes de prueba = [AN0, 120, 140, 30, 168, 255, 10, 15]. Donde AN0 es el valor de un canal analógico del microcontrolador PIC18F2680.

A su vez envía por medio del UART dichos valores a una hyperterminal.

El nodo A escucha los mensajes de la red e imprime los valores en el LCD, enviando de igual manera vía UART los datos a otra hypeterminal.

La conexión con el USB-Serial para los nodos será:

Conectamos TxD --> RxD, TxD --> RxD y GND --> GND


CONFIGURACIÓN PARA 500 Kbps de los Nodos A y B:

Para 500 Kbps:
Tq = 0.2 us
Tq = (2 x (PRE + 1))/Fosc
PRE = ((Tq x Fosc) / 2 ) - 1
PRE = ((0.2us x 20Mhz) / 2) - 1 = 1

Baudaje = 10 x 0.2 us = 2 us = 1/(2 us) = 500 kbps

TOTAL-----------> 10 Tq

CAN_BRG_PRESCALAR 1
CAN_BRG_SYNCH_JUMP_WIDTH 0 ----> 1
CAN_BRG_PHASE_SEGMENT_1 2 ----> 3
CAN_BRG_PROPAGATION_TIME 2 ----> 3
CAN_BRG_PHASE_SEGMENT_2 2 ----> 3



SW NODO A:

#include <18F2680.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#define LCD_DB4   PIN_C2   //PIN 11 LCD          PIN 7,8,9,10 DEL LCD NO SE CONECTAN
#define LCD_DB5   PIN_C3   //PIN 12 LCD          PIN 1 A TIERRA
#define LCD_DB6   PIN_C4   //PIN 13 LCD          PIN 2 A VCC
#define LCD_DB7   PIN_C5   //PIN 14 LCD          PIN 3 R=330 OHMS A TIERRA
#define LCD_RS    PIN_C1   //PIN 4 LCD
//#define LCD_RW    PIN_A2   // a tierra
#define LCD_E     PIN_C0   //PIN 6 LCD

#include "flex_lcd.c"
#include "can-18f4580_mod.c"

int16 cnt=0;

#int_timer2
void isr_timer2(void)
{
   if(cnt >1000)
   {
      output_toggle(PIN_B5);
      cnt = 0;
   }
   cnt++;
}

void main()
{
   struct rx_stat rxstat;
   int32 rx_id;
   int in_data[8];
   int rx_len;
   int i;
      
   // (4   *   4    *   (79+1)*16)/20x10^6 = 1.02 ms
   setup_timer_2(T2_DIV_BY_4,79,16);   //setup up timer2 to interrupt every 1ms if using 20Mhz clock
   enable_interrupts(INT_TIMER2);   //enable timer2 interrupt
   enable_interrupts(GLOBAL);       //enable all interrupts (else timer2 wont happen)
  lcd_init();
   printf(LCD_PUTC,"\f  CAN BUS 500Khz");
   printf(LCD_PUTC,"\n  Carlos - 2017 ");
   delay_ms(3000);
  
   for (i=0;i<8;i++)
   {
      in_data[i]=0;
   }

   printf("\r\n\r\nCCS CAN EXAMPLE  --> MASTER\r\n");

   can_init();

  // can_set_mode(CAN_OP_LOOPBACK); // Added this line

   printf("\r\nRunning...");

   while(TRUE)
  {
      if (can_kbhit())   //if data is waiting in buffer...
      {
         if(can_getd(rx_id, &in_data[0], rx_len, rxstat))
         { //...then get data from buffer
            printf("\r\nGOT: BUFF=%U ID=%LU LEN=%U OVF=%U ", rxstat.buffer, rx_id, rx_len, rxstat.err_ovfl);
            printf("FILT=%U RTR=%U EXT=%U INV=%U", rxstat.filthit, rxstat.rtr, rxstat.ext, rxstat.inv);
            printf("\r\n    DATA = ");
            for (i=0;i<rx_len;i++)
            {
               printf("%u ",in_data[i]);
            }
            printf(LCD_PUTC,"\fID = %Ld, LEN = %d", rx_id, rx_len);
            printf(LCD_PUTC,"\n%u %u %u %u %u",in_data[0],in_data[1],in_data[2],in_data[3],in_data[4]);
           
            printf("\r\n");
         }
         else
         {
             printf("\r\nFAIL on GETD\r\n");
             printf(LCD_PUTC,"\fFAIL on GETD");
         }
      }
     
   }
}


SW NODO B (ID = 24):


#include <18F2680.h>
#fuses HS,NOPROTECT,NOLVP,NOWDT
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#include "can-18f4580_mod.c"

int16 ms=0, cnt=0;

#int_timer2
void isr_timer2(void)
{
   ms++; //keep a running timer that increments every milli-second
   if(cnt >1000)
   {
      output_toggle(PIN_B5);
      cnt = 0;
   }
   cnt++;
}

void main()
{

//send a request (tx_rtr=1) for 8 bytes of data (tx_len=8) from id 24 (tx_id=24)
   int out_data[8];
   int32 tx_id=24;
   int1 tx_rtr=0;
   int1 tx_ext=1;
   int tx_len=8;
   int tx_pri=3;

   int i;

   out_data[0]=0;
   out_data[1]=120;
   out_data[2]=140;
   out_data[3]=30;
   out_data[4]=168;
   out_data[5]=255;
   out_data[6]=10;
   out_data[7]=15;
  
   // (4   *   4    *   (79+1)*16)/20x10^6 = 1.02 ms
   setup_timer_2(T2_DIV_BY_4,79,16);   //setup up timer2 to interrupt every 1ms if using 20Mhz clock

   can_init();

   enable_interrupts(INT_TIMER2);   //enable timer2 interrupt
   enable_interrupts(GLOBAL);       //enable all interrupts (else timer2 wont happen)
 
   setup_adc_ports(AN0);
   setup_adc(ADC_CLOCK_INTERNAL);
   set_adc_channel(0);
  
   delay_ms(3000);

   printf("\r\n\r\nCCS CAN EXAMPLE --> SLAVE\r\n");
   printf("\r\nRunning...");
   
   while(TRUE)
   {
      //every two seconds, send new data if transmit buffer is empty
      if ( can_tbe() && (ms > 1000))
      {
         ms=0;
         out_data[0] =Read_ADC();
         for (i=0;i<tx_len;i++)
         {
             printf("%u ",out_data[i]);
         }
          printf("\r\n");
         i=can_putd(tx_id, out_data, tx_len,tx_pri,tx_ext,tx_rtr); //put data on transmit buffer
      }
   }
}

Nota: Los archivos .hex de ambos nodos se encuentran en la carpeta del proyecto Practica #20

NODO A 
(RECIBIENDO)


 NODO B (ID = 24)
 (ENVIANDO)
1er Byte valor de potenciometro


NODO A Y B COMUNICÁNDOSE CORRECTAMENTE!


Toda la información de implementación CAN sobre microcontroladores microchip la puedes encontrar en el siguiente link:


6.- Conexión entre la tarjeta YRDKRX62N (NODO C) y los Nodos A y B:


7.- El cálculo del baudrate para el Nodo C se especifica en el archivo config_r_can_rapi.h:

  /*
        Desired baudrate 500 kbps.
        Selecting prescale to 4.
        fcanclk = 48000000/4
        fcanclk = 12000000 Hz
        Bitrate = fcanclk/Tqtot
        or,
        Tqtot = fcanclk/bitrate
        Tqtot = 12000000/500000
        Tqtot = 120/5 = 24.
        Tqtot = TSEG1 + TSEG2 + SS
        Using TSEG1 = 15 Tq
          TSEG2 = 8 Tq
          SS = 1 Tq always
          Re-synchronization Control (SJW) should be 1-4 Tq (must be <=TSEG2).
    */
    #define CAN_BRP     4
    #define CAN_TSEG1   15
    #define CAN_TSEG2   8
    #define CAN_SJW     2

8.- Como todos los Nodos A, B y C utilizan el formato extendido ID. En el archivo config_r_can_api.h se establecerá dicha configuración:

/* Frame ID mode: Standard ID, Extended ID, or Mixed ID mode */
/* Must choose 1 and only 1 from: */
//#define FRAME_ID_MODE   STD_ID_MODE
#define FRAME_ID_MODE   EXT_ID_MODE
//#define FRAME_ID_MODE   MIXED_ID_MODE

9.- Las funciones de inicializacion y polling se encuentran en api_demo.c:

uint32_t InitCanApp(void);
void CheckCANerrors(void);
void HandleCanBusState(uint8_t ch_nr);
void CanPollDemo(void);

10.- La función principal main.c queda de la siguiente forma:

void main(void)
{
            set_ipl(0);              // enable interrupts
            SR_Oscilador();          //  F = 96 Mhz
            SR_LCD_GRAPH();          //  LCD 96x64
            SR_TIMER_0();            // Inicializa el Timer 0 en cascada para 16 bits
            SR_CAN_INIT();           // inicializa Bus CAN

            while(1)
            {
                        /* Check for CAN errors. */
                        CheckCANerrors();

                        if (can_state[0] != R_CAN_STATUS_BUSOFF)
                        {
                                    CanPollDemo();
                        }
                        else
                        /* Bus Off. */
                        {
                                    LCDPrintf(0, 0, "App in      ");
                                    LCDPrintf(1, 0, "Bus Off     ");
                                    /* HandleCanBusState() will restart app. */
                                    delay_ms(3000);
                                    LCDClear();
                        }
            }

}

  • Agregar código, compilar y debug:
1.- Bajar el código de:
--> Practica #20

2.- Compilar con el icono del martillo y debug con el icono del insecto:

RESULTADOS:

Enviando a la red CAN los frames [54, 11, 22, 33, 44, 55, 66, 77] con ID = 33


Recibiendo de la red CAN los frames [An0, 120, 140, 30, 168, 255, 10, 15] con ID = 24

No hay comentarios.:

Publicar un comentario