Autor Tema: Ayuda comunicación I2C PIC18F252  (Leído 2328 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Ayuda comunicación I2C PIC18F252
« en: 26 de Septiembre de 2017, 13:24:10 »
Hola compañeros, bueno estoy realizando una comunicación por I2C entre un pic 18F252 y un PCF8574 pero mi codigo no tiene buenos resultados.
Lei el funcionamiento del pic 18f252 como maestro y seguí el ejemplo típico de uso. 
El codigo que tengo es el siguiente:

Código: C
  1. #include "fuses_PIC.h"
  2.  
  3. #define _XTAL_FREQ 4000000
  4. #define  envio_dato 0x00
  5. #define  envio_direccion 0x01
  6. #define  termino_comuncacion 0x02
  7. #define  direccion_PCF8574 0b01000000
  8. #define  NO_STOP_COMUNICACION  0b0
  9. #define  STOP_COMUNICACION  1
  10.  
  11. unsigned char volatile address;
  12. unsigned char volatile addres_or_data_or_out = 0x00;
  13. unsigned char volatile data;
  14.  
  15. void config_pines_botom(){
  16.     PORTB = 0x00;       // Limpio puerto
  17.     //LATB = 0x00;
  18.     TRISB = 0b00000001;      // pin RB0 como entrada
  19.     PORTBbits.RB1 = 1;       // prendo un led de encendido pic
  20. }
  21.  
  22.  
  23. void config_pines_I2C(){
  24.     PORTC = 0x00;       // Limpio puerto
  25.     TRISCbits.RC4 = 0;  // SDA salida
  26.     TRISCbits.RC3 = 0;  // SCL salida    
  27. }
  28.  
  29. void config_I2C(){    
  30.     SSPSTATbits.SMP = 1;
  31.     SSPCON1 = 0x38;     // 3=0011   8=1000    
  32.     SSPADD = 0x28 ;     // para 4Mhz tengo 100khz CLK  de I2C  
  33. }
  34.  
  35. void interrupt I2C_VECTOR(){
  36.     PIR1bits.SSPIF = 0; // limpio bandera
  37.     if((addres_or_data_or_out == envio_direccion) && (SSPCON2bits.PEN == NO_STOP_COMUNICACION)){ // interrupcion nº1 y no stop comunicacion?
  38.         SSPBUF = address;   // Ingreso direccion del slave    
  39.         addres_or_data_or_out = envio_dato;  // proxima interrup sera de ack para dato
  40.     }
  41.     if((addres_or_data_or_out == envio_dato) && (SSPCON2bits.PEN == NO_STOP_COMUNICACION)){      // interrupcion nº2 y no stop comunicacion?  
  42.         SSPBUF = data;          // Ingreso dato a enviar
  43.         addres_or_data_or_out = termino_comuncacion;  // proxima interrup sera de ack para termino comunicacion
  44.         SSPCON2bits.PEN = STOP_COMUNICACION;
  45.     }  
  46.     if((addres_or_data_or_out == termino_comuncacion) && (SSPCON2bits.PEN == STOP_COMUNICACION)){  // interrupcion por termino de comunicacion?
  47.         addres_or_data_or_out = envio_direccion;  // limpio variable para nueva comunicacion    
  48.         SSPCON2bits.PEN = NO_STOP_COMUNICACION;
  49.     }
  50. }
  51.  
  52. void enviar_dato(){
  53.     SSPCON2bits.SEN = 1;        // INICIO coomunicacion        
  54. }
  55.  
  56. void config_interrupciones(){
  57.     RCONbits.IPEN = 0;         // sin prioridad
  58.     PIR1bits.SSPIF = 0;         // limpio bandera
  59.  //   IPR1bits.SSPIP = 1;         // seteo prioridad
  60.     PIE1bits.SSPIE = 1;         // habilito interrupcion
  61.     INTCONbits.PEIE_GIEL = 1;   // habilitar interrupcion periferica
  62.     INTCONbits.GIE_GIEH = 1;    // habilitar interrupciones globales  
  63. }
  64. void main(){
  65.     config_pines_I2C();
  66.     config_pines_botom();
  67.    
  68.     config_I2C();  
  69.     config_interrupciones();
  70.    
  71.     while(1){
  72.         if(PORTBbits.RB0 == 0){
  73.             data = 0b10101101;  // dato a enviar
  74.             enviar_dato();      // enviar dato
  75.             __delay_ms(500);            
  76.         }
  77.         if(PORTBbits.RB0 == 0){
  78.             data = 0b01010010;  // dato a enviar
  79.             enviar_dato();      // enviar dato
  80.             __delay_ms(500);            
  81.         }
  82.     }
  83. }

Estoy utilizando un botom para enviarlos valores hacia el PCF8574 y observarlo con leds en la salida.

Simule el código en el proteus y realice un debbug, y he notado que no ingresa a la interrupción. Este pic tiene prioridades y trate hacerlo de esa forma, de un inicio, pero lo cambie a que funcionara la interrupción como los pics 16fxxx. (mas adelante quisiera hacerlo que funcionara con prioridades).
No se que esta mal, si es la interrupción o la configuración del I2C.  :(

Cualquier ayuda me sera util.

Saludos

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #1 en: 26 de Septiembre de 2017, 16:42:39 »
Vamos sacando algunos problemas

Código: C
  1. void config_pines_botom(){
  2.     PORTB = 0x00;       // Limpio puerto
  3.     //LATB = 0x00;
  4.     TRISB = 0b00000001;      // pin RB0 como entrada
  5.     PORTBbits.RB1 = 1;       // prendo un led de encendido pic
  6. }

En un PIC 18, cuando intentas escribir al puerto, usas el registro LATx, y para leer el estado del pin los registros PORTx

I2C:

Creo que lo que intentas implementar es una maquina de estado. Vas a tener que tener un variable la cual le indicas que estas en el START, en tu interrupcion preguntas con un switch case sobre esa variable, y de acuerdo a donde estas, es lo que haces. Siguiendo el paso a paso que da el datasheet:

Citar
1. The user generates a START condition by setting the START enable bit, SEN (SSPCON2<0>).
2. SSPIF is set. The MSSP module will wait the required start time before any other operation takes place.
3. The user loads the SSPBUF with the slave address to transmit.
4. Address is shifted out the SDA pin until all 8 bits are transmitted.
5. The MSSP Module shifts in the ACK bit from the slave device and writes its value into the SSPCON2 register (SSPCON2<6>).
6. The MSSP module generates an interrupt at the end of the ninth clock cycle by setting the SSPIF bit.
7. The user loads the SSPBUF with eight bits of data.
8. Data is shifted out the SDA pin until all 8 bits are transmitted.
9. The MSSP Module shifts in the ACK bit from the slave device and writes its value into the SSPCON2 register (SSPCON2<6>).
10. The MSSP module generates an interrupt at the end of the ninth clock cycle by setting the SSPIF bit.
11. The user generates a STOP condition by setting the STOP enable bit PEN (SSPCON2<2>).
12. Interrupt is generated once the STOP condition is complete.

Supongamos que lo que necesitamos enviar sea:

Start - Address+R/W - Data - Stop

Entonces vamos a comenzar con un Start en el programa principal pero ademas vamos a agregarle un variable mas.

Código: C
  1. enum I2C_STATE {
  2.     I2C_IDLE,
  3.     I2C_START,
  4.     I2C_ADDRESS,
  5.     I2C_DATA,
  6.     I2C_STOP
  7. };
  8.  
  9. enum I2C_STATE i2c_state = I2C_IDLE;
  10.  
  11. void main(void)
  12. {
  13.     i2c_state = I2C_START;
  14.     SSPCON2bits.SEN = 1;  // Genero START
  15.     while(1);
  16. }
  17.  
  18. void interrupt ISRvector(void)
  19. {
  20.      PIR1bits.SSPIF = 0; // limpio bandera
  21.      switch(i2c_state)
  22.      {
  23.          case I2C_START:
  24.                 //Aca el START ya paso, asi que lo siguiente es poner la direccion a enviar + el bit R/W en el buffer
  25.                SSPBUFF = 0x31;  // El numero va a depender de que estes haciendo obviamente. Yo puse 0x31 como para poner algo
  26.                i2c_state = I2C_DATA;   // Cuando se termine de enviar, entra de nuevo a pedir el dato
  27.                break;
  28.          case I2C_DATA:
  29.                 // Ya enviamos la direccion, ahora necesitamos enviar el dato. Podriamos revisar aca tambien si se recibio el ACK o un NACK de la direccion y actuar acorde
  30.                SSPBUFF = 0x88;  // Dato a enviar 0x88
  31.                i2c_state = I2C_STOP;   // Cuando se termine de enviar, entra de nuevo a pedir el dato
  32.                break;
  33.          case I2C_STOP:
  34.                 // Enviamos dato y direccion. Podriamos revisar aca tambien si se recibio el ACK o un NACK del dato
  35.                i2c_state = I2C_IDLE;   // Cuando se termine de enviar, entra de nuevo a pedir el dato
  36.                break;
  37.          default:
  38.                // Aca IDLE, o cualquier otra cosa que pueda suceder.
  39.                break;
  40.      }
  41. }

Esto lo arme en 5 minutos, asi que no espero que funcione, pero espero que te arroje un poco mas de luz, si buscas I2C state machine, seguramente tengas un codigo mas completo.
Ya que esto no tiene en cuenta muchas cosas.
« Última modificación: 26 de Septiembre de 2017, 17:05:27 por KILLERJC »

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #2 en: 29 de Septiembre de 2017, 23:06:39 »
Gracias KillerJC, me funciono el código con tus correciones (perdón por el atraso de la respuesta esta un poco ocupado).

Me pareció excelente la forma de hacer comunicación I2C con una maquina de estados. Solo había hecho la comunicación I2C en ASM y no lo había hecho de forma tan clara.

Killer... ¿En que otros casos recomiendas usar una maquina de estados?, por ejemplo yo ahora en adelante la usare en comunicación I2C, SPI y UART  8)

Saludos!!

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #3 en: 30 de Septiembre de 2017, 11:45:46 »
Citar
Killer... ¿En que otros casos recomiendas usar una maquina de estados?, por ejemplo yo ahora en adelante la usare en comunicación I2C, SPI y UART  8)

No es para usarlo en todo, pero a veces te simplifica las cosas. Asi que lo recomendaria en caso que simplifique las cosas, o en caso que sea necesario. Implementar una maquina de estado general para I2C es mucho mas largo. No se tiene en cuenta los AKC/NACK, si se debe repetir el start, etc.
Ejemplos de comunicaciones sobre la UART, suponete que recibis una trama de un GPS (bastante visto en el foro por un tiempo):

Código: [Seleccionar]
$GPGGA,092750.000,5321.6802,N,00630.3372,W,1,8,1.03,61.7,M,55.2,M,,*76
$GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A
$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70
$GPGSV,3,2,11,02,39,223,19,13,28,070,17,26,23,252,,04,14,186,14*79
$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76
$GPRMC,092750.000,A,5321.6802,N,00630.3372,W,0.02,31.66,280511,,,A*43
$GPGGA,092751.000,5321.6802,N,00630.3371,W,1,8,1.03,61.7,M,55.3,M,,*75
$GPGSA,A,3,10,07,05,02,29,04,08,13,,,,,1.72,1.03,1.38*0A
$GPGSV,3,1,11,10,63,137,17,07,61,098,15,05,59,290,20,08,54,157,30*70
$GPGSV,3,2,11,02,39,223,16,13,28,070,17,26,23,252,,04,14,186,15*77
$GPGSV,3,3,11,29,09,301,24,16,09,020,,36,,,*76
$GPRMC,092751.000,A,5321.6802,N,00630.3371,W,0.06,31.66,280511,,,A*45

Sabes que comienza con el $, luego esta todo separado por comas y termina con un \r o \r\n

Una forma simple seria guardar toda la trama hasta detectar otro $, el cual nos indica cada nueva trama. De alli hacerla pasa por un strtok para separarlo. Pero tal ves muchos de los string no sean necesarios, y solo te interese el GPRMC por ejemplo. Usar el metodo anterior es posible, pero implica tener que guardar toda la trama para luego su procesamiento, lo cual lo vas a tener que realizar fuera de la interrupcion por lo que demoraria.

Entonces podes hacer una maquina de estados que espere caracter a caracter, ejemplo, espere el $, luego la G, la P, la R, la M,y luego la C. Si alguno de esos NO se cumple, directamente lo volves a que espere el $ de vuelta. Eso no lo almacenas. Luego con las comas podes ir separando los datos, si queres creas tambien estados, como por ejemplo latitud, longitud, velocidad, checksum, etc. Las cuales vas a ir  ubicando en cada array por separado.

Hiciste lo mismo que antes con el strtok, pero esta ves lo hiciste a medida que recibiste los caracteres.

-------------------------------

Eso respecto a comunicaciones, podes hacer un menu si queres.

Código: C
  1. switch(estado_menu)
  2. {
  3.   case 0:
  4.        //Muestro pantalla principal
  5.        if( boton_presionado ) estado_menu = 1;
  6.        break;
  7.   case 1:
  8.       // Muestro menu principal
  9.       if (boton1_presionado) estado_menu= 2;
  10.       if (boton2_presionado) estado_menu= 3;
  11.      break;
  12.     .........
  13. }

Por supuesto, no es la mejor forma. ya que estaria "mostrando" continuamente... Pero se podria mejorar a algo asi:

Código: C
  1. while(1)
  2. {  botones=Sensar_botones(); // Aca se leen los botones, antirebote aca.
  3.   if(botones) MaquinaMenu(botones); // Aca no se leen botones, solo se muestra
  4. }

Lo bueno de hacerlo asi, es que podes hacer que la funcion de sensar botones, devuelva valores de los botones solo cuando algo sea presionado y que cambie desde la ultima ves que se senso. Es decir si en la primer vuelta del while se detecta que estaba presionado RB0 va a guardar ese valor, entonces cuando inicie la segunda vuelta, y detecte que sigue RB0 presionado, que era lo mismo que antes tenia presionado, va a devolver un 0. Asi no se toma en cuenta 2 o mas veces.

--------------------------------------

El switch case solo acepta numeros, o si usas caracteres 'i' seria su valor ASCII. Por eso es que uso el enum, para darle nombres y no usar 0,1,2,3 a pesar que internamente para C sean numeros 0,1,2,3,... pero para el que hace el programa sea mas legible usando el enum.
« Última modificación: 30 de Septiembre de 2017, 11:51:06 por KILLERJC »

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #4 en: 30 de Septiembre de 2017, 16:17:13 »
Gracias por los consejos Killer, pude terminar el programa que era una comunicación I2C entre el pic y un LCD,
pero le quiero agregar la UART para una comunicación Asincrona pero tengo problemas con la recepcion de información.

Me hice un programa a parte para validar la UART tanto su TX como RX. Estoy simulando con el ISIS, Hyperteminal y El virtual port.
El TX me funciona superbien' pero el RX no.
El programa recive un caacter y luego lo renvia, y debiera verlo en el Hyperterminal.

Código: C
  1. #include "fuses_pic18.h"
  2.  
  3. #include <xc.h>
  4. #define _XTAL_FREQ 4000000
  5.  
  6. void pines_UART(){
  7.     TRISCbits.TRISC6 = 0;           // Salida TX
  8.     TRISCbits.TRISC7 = 1;           // Entrada RX  
  9. }
  10.  
  11. void config_TX(){
  12.    
  13.     SPBRG = 12;                    //  2,4 Kbps = 2400 bps  25..... 19200 Bps usa 12 y alta veocidad
  14.    
  15.     RCSTAbits.SPEN = 1;
  16.     TXSTA = 0b00000000;
  17.    
  18.     TXSTAbits.BRGH = 1;             //alta velocidad = 1, baja = 0
  19.     TXSTAbits.TXEN = 1;             // Habilito TX      
  20. }
  21.  
  22. void enviar_String(char info[]){
  23.     int volatile i = 0;
  24.     while(info[i] != '\0'){
  25.         TXREG = info[i];
  26.         while(TXSTAbits.TRMT == 0);     // espero que tranmita todo
  27.         asm("nop");
  28.         i++;
  29.     }  
  30. }
  31.  
  32. void config_RX(){
  33.    
  34.     SPBRG = 12;                    //  2,4 Kbps = 2400 bps  25..... 19200 Bps usa 12 y alta veocidad
  35.    
  36.     TXSTAbits.SYNC = 0;
  37.     RCSTAbits.SPEN = 1;    
  38.     TXSTAbits.BRGH = 1;             //alta velocidad = 1, baja = 0
  39.    
  40.     RCSTAbits.CREN = 1;             // habilitar recepcion
  41.    
  42. }
  43.  
  44. void config_Interrupciones(){
  45.     RCONbits.IPEN = 0;          // Sin prioridad prioridad
  46.    
  47.     PIR1bits.RCIF = 0;         // limpio bandera RX.
  48.    
  49.     IPR1bits.RCIP = 0;         // Sin priorida bit individual
  50.  
  51.     PIE1bits.RCIE = 1;         // habilito interrupcion RX
  52.    
  53.     INTCONbits.PEIE_GIEL = 1;   // habilitar interrupcion periferica
  54.     INTCONbits.GIE_GIEH = 1;    // habilitar interrupciones globales
  55. }
  56.  
  57. void interrupt RX_interrupcion(void){  
  58.     RCSTAbits.CREN = 0;             // desabilitar recepcion
  59.     char volatile letra[1];
  60.     LATBbits.LATB1 = 1;       // prendo un led de encendido pic
  61.     __delay_ms(1000);
  62.     PORTBbits.RB1 = 0;
  63.     enviar_String(letra[0]);     //  Envio  lo que recivi  
  64.    
  65.    
  66.     RCSTAbits.CREN = 0;             // habilito recepcion
  67.    
  68.     PIR1bits.RCIF = 0;         // limpio bandera RX.
  69.    
  70. }
  71. void main(void) {
  72.     pines_UART();
  73.     config_TX();
  74.     config_RX();
  75.     config_Interrupciones();
  76.     //enviar_String("Hola Mundo\n\r");
  77.     asm("clrf RCREG");
  78.     while(1){      
  79.     }      
  80. }

La comunicación esta 19200 bps (simulo con un clk de 4MHz).

En la rutina de interrupción puse un delay para que la simulación mediera el tiempo de ingresar a ella, ya que sin eso no podía). Bueno me di cuenta que el registro RCREG siempre me esta reciviendo el fin de cadena osea un '\0', y no he podido recivir bien el caracter.

Cualquier ayuda me sera util.
 
Saludos

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #5 en: 30 de Septiembre de 2017, 18:13:41 »
NUNCA pero nunca pongas un delay en una interrupcion. Tampoco uses funciones que tome demasiado tiempo (como un while )

No es necesario desactivar la recepcion. Y si usas C, no deberias estar poniendo instrucciones en ASM, al menos no en XC8.
Intenta agrupar todo lo referente a un modulo en una funcion, es decir la UART, que cambie los pines + interrupcion + configuracion, de todo ese modulo en una funcion. Es mas simple, ya que si tenes 2 modulos tendrias ya como 4 funciones ( disminuye la legibilidad).
Al leer RCREG que es donde llega el caracter de recepcion, se limpia sola el flag de interrupcion por RX, asi que no hace falta limpiarlo.

Este codigo "deberia" (no lo probe) funcionar, para darte un simple echo, es decir lo que envies al PIC va a ser respondido y ademas cada dato va a hacer cambiar el estado del led (el cual

Código: C
  1. #include "fuses_pic18.h"
  2.  
  3. #include <xc.h>
  4. #define _XTAL_FREQ 4000000
  5.  
  6.  
  7. void config_UART(void){
  8.  
  9.     TRISCbits.TRISC6 = 0;           // Salida TX
  10.     TRISCbits.TRISC7 = 1;           // Entrada RX  
  11.  
  12.     SPBRG = 12;                    //  2,4 Kbps = 2400 bps  25..... 19200 Bps usa 12 y alta veocidad
  13.    
  14.     TXSTA = 0;
  15.     TXSTAbits.BRGH = 1;             //alta velocidad = 1, baja = 0
  16.     TXSTAbits.TXEN = 1;             // Habilito TX      
  17.     RCSTAbits.SPEN = 1;            
  18.     RCSTAbits.CREN = 1;             // habilitar recepcion
  19.  
  20.     PIR1bits.RCIF = 0;         // limpio bandera RX.
  21.     IPR1bits.RCIP = 0;         // Sin priorida bit individual
  22.     PIE1bits.RCIE = 1;         // habilito interrupcion RX
  23.  
  24. }
  25.  
  26. void config_Interrupciones(){
  27.  
  28.     RCONbits.IPEN = 0;          // Sin prioridad prioridad
  29.     INTCONbits.PEIE_GIEL = 1;   // habilitar interrupcion periferica
  30.     INTCONbits.GIE_GIEH = 1;    // habilitar interrupciones globales
  31.    
  32. }
  33.  
  34. void interrupt RX_interrupcion(void){  
  35.  
  36.     LATBbits.LATB1 ^= 1;        // alterno el led
  37.     TXREG = RCREG;              // Lo que llego ( a RCREG) lo pongo en TXREG para que sea enviado
  38. }
  39.  
  40. void main(void) {
  41.  
  42.     TRISBbits.TRISB1 = 0;
  43.  
  44.     config_UART();
  45.     config_Interrupciones();
  46.  
  47.     while(1);
  48.  
  49. }

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #6 en: 30 de Septiembre de 2017, 19:10:08 »
Buenas, me funciono era super simple pasar el carácter jajaaj (lo que pasa es que quería hacer un programa mas estándar por eso trataba de almacenar el dato en string).

KIller de lo que me has comentado me gustaría saber algunas cosas si es posible (para poder ir aprendiendo).

Lo primero es acerca de las instrucciones en ASM, en que caso es recomendable usarlas.
te doy un ejemplo, la instrucción nop se demora 1 ciclo de ejecución que en tiempo seria 1us (para un cristal de 4MHz). Si yo tuviera que hacer un delay de ese tiempo, ¿seria mejor usar la función __delay_us(1) o asm("nop")?

Unida a la pregunta anterior,¿por que no recomendarias usar ASM en XC8? ¿En otros compiladores tampoco lo recomendarías o no?

Otra pregunta es acerca de las funciones que demoran demasiado tiempo.
Tu me has dicho que una de ellas es el while, pero ¿Como podría saber cuanto se demoran esas funciones,para poder optimizar el código?

y mi ultima pregunta es acerca es acerca de la funciones estandar printf() y scanf(). ¿esas funciones sirven para utilizar la UART? (por ejemplo,si uso printf("hola mundo") la función sola configura todo (registros) y envia el  string  xD)

Cualquier info me sera de utilidad para seguir aprendiendo.

Saludos

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #7 en: 30 de Septiembre de 2017, 20:31:23 »
Citar
Lo primero es acerca de las instrucciones en ASM, en que caso es recomendable usarlas.
Citar
Unida a la pregunta anterior,¿por que no recomendarias usar ASM en XC8? ¿En otros compiladores tampoco lo recomendarías o no?


ASM unicamente lo usarias en caso de que quieras hacer algo muy especifico o si quisieras tener un mayor control sobre como se hace, o reducir los tiempos al maximo, cuando me refiero a control, es a la cantidad de ciclos ocupados de forma exacta por el codigo. Casi siempre es mas simple usar otro micro mejor que estar tan jugado con los tiempos. Por ejemplo los cambios de tareas en freeRTOS estan realizados en ASM, para los nucleos ARM.

El que uses ASM reduce la portabilidad/legibilidad del codigo. Si tu codigo esta bien realizado te vas a encontrar que vas a tener todo lo referente a las comunicaciones/configuracion/etc por un lado que es lo que pertenece al micro en cuestion, y por otro lado vas a tener todas las funciones escritas en C, de tal forma que no tenga dependencia del micro que sea que le pongas, incluso podes ponerlo en la PC y vas a ejecutarlo. Ademas no hay necesidades de usar ASM, lo que podes hacer en ASM lo podes hacer en C de forma mas rapida, excepto las funciones DSP de dsPIC, pero Microchip ofrece una funcion C para cada una de esas instrucciones en ASM, el unico caso "obligatorio" que conozco.

Citar
te doy un ejemplo, la instrucción nop se demora 1 ciclo de ejecución que en tiempo seria 1us (para un cristal de 4MHz). Si yo tuviera que hacer un delay de ese tiempo, ¿seria mejor usar la función __delay_us(1) o asm("nop")?
Usa __delay_us(1), el compilador se encarga de realizar la rutina para la cantidad de ciclos correctos, es decir un nop. Si observas no podes hacer algo asi:

Código: C
  1. i=10;
  2. __delay_us(i);

Algo que podes realizar en CCS, pero en XC8 no, ya que espera una constante, para hacer la subrutina que cumpla con los ciclos necesarios. Si usaste ASM vas a saber que crear una rutina que de de forma exacta la cantidad de ciclos en una demora y que sea variable es bastante complicado. Ya que hacerlo con un for, solo aumentaria el error. Por lo tanto en XC8 si queres crear una demora, la cual NO va a ser exacta, podes hacerlo:

Código: C
  1. void delay_s(uint16_t t)
  2. {
  3.     while(t--) __delay_ms(1000);
  4. }

Citar
Tu me has dicho que una de ellas es el while, pero ¿Como podría saber cuanto se demoran esas funciones,para poder optimizar el código?

El mayor problema de poner algo que demore la interrupcion, es que cuando entro a la interrupcion, si no sale por estar ejecutando alguna funcion y aparece otra interrupcion, esta no va a ser atendida hasta que termine la primera, vamos a un ejemplo mas simple y que ocurria con tu programa.
Entraba a la interrupcion apenas recibia 1 caracter. Supongamos que para entrar por la UART un caracter a esos baudios tarda 10ms ( para hacer las cosas redondas ), vos pusiste un delay de 1000ms, eso quiere decir que se perderian casi 100 caracteres, caracteres que siguen llegando. Ademas la UART posee una proteccion, cuando llega un caracter y no se recoge a tiempo (ya que llego otro) se activa un bit de Overrun y se detiene todo el proceso de recepcion, hasta que deshabilites y vuelvas a habilitar el recepcion. Entonces la idea basica es: Recibir el caracter y salir rapido, para que cuando llegue el otro estemos fuera de la interrupcion. Y esto se cumple para TODO, imaginate un Timer con interrupcion cada 100ms y vos con un delay de 1000ms dentro, eso quiere decir que el codigo del Timer va a ejecutarse primer a los 100ms de activado, y luego cuando termine el delay de 1000 va a volver a entrar. Esto aparte te deja "famélico" en tiempo de procesador a tu loop principal, ya que siempre va a estar reentrando a la interrupcion y nunca se va a ejecutar el while, si no crees probalo:

Código: C
  1. void interrupt ISR(void)
  2. {
  3.     PIR9bits.TMRIF = 0;    // Borro flag
  4.     LATBbits.LATB0 ^= 1;
  5.     __delay_ms(1000);
  6. }
  7.  
  8. void main(void)
  9. {
  10.     ConfigTMR();   //Interrupcion cada 100ms
  11.     ConfigPin();
  12.     ActivoInterrupciones();
  13.     while(1)
  14.     {
  15.         __delay_ms(500);        // Observaras que va a quedar aca siempre., nunca va a encenderse/apagarse RB1
  16.         LATBbits.LATB1 ^= 1;
  17.     }
  18. }

Un while puede ser rapido, pero pensemos por un momento, tanto la recepcion como la transmision dura lo mismo en enviarse un caracter ( suponiendo que los relojes sean iguales ) Si vos dentro de la interrupcion queres enviar 2 o 3 caracteres por cada 1 caracter recibido, te vas a dar cuenta que hay 1 o mas caracteres que se perderian, ademas de tener que lidiar con el problema de Overrun ( el cual no deberia ocurrir nunca ), pero por ejemplo yo puedo hacer sin problemas:

Código: C
  1. i=0
  2. while(i++<10)
  3. {
  4.    LATBbits.LATB0 ^= 1;
  5. }

Use un while, pero el loop es muy simple y se va a ejecutar rapidamente. El problema no es el while, el problema es que no se quede por siempre alli, el problema es Arjona

Citar
y mi ultima pregunta es acerca es acerca de la funciones estandar printf() y scanf(). ¿esas funciones sirven para utilizar la UART? (por ejemplo,si uso printf("hola mundo") la función sola configura todo (registros) y envia el  string  xD)

scanf() no existiria en el mundo de los microcontroladores. A no ser que poseas un OS con un buffer de entrada, pero para cuando lleguemos a eso estariamos hablando seguramente de un microprocesador con algun Linux. O a no ser que lo implementes y crees tu propio scanf
printf() por su parte creo que XC8 provee su soporte, lo que si tenes que definir una funcion putch(), ejemplo:

Código: C
  1. void putch(char c)
  2. {
  3.     while(!TXSTAbits.TRMT);
  4.     TXREG = c;
  5. }

Para que entiendas, printf lo unico que hace es "decodificar" el string. si tenias:

Código: C
  1. i= 6;
  2. printf("i = %d",i);

el printf va a usar el putch , y va a pasar letra por letra, primero la 'i',' ','=',' ','6' , es decir los caracteres ya formateados.
¿ Es rapido ?, Usualmente es bastante lento el printf, ya que pasar los numeros a caracteres por ahi no es tan simple, enteros no es tanto problema, pero flotantes si es lento.
Otro problema que ves es que en el envio (que usa el printf y que fabricamos) esperamos que se envie caracter por caracter, es decir que va a salir del printf recien cuando termine de enviar todo.

Una cosa mas, como nosotros creamos el putch, podemos mostrarlo en cualquier lado. Puedo usar el SPI, o enviarlo a un LCD, etc

-----------------------------------------------------

Hasta ahora vimos que la recepcion se puede hacer simple y sin esperar, pero la transmision estamos siempre trabados ahi.

Una forma es crear buffers ( si son circulares mas simple su control) de escritura y lectura

Código: C
  1. volatile char bufferRX[64];
  2. volatile char bufferTX[64];

Ahora cuando entremos a la interrupcion del RX vamos a obviamente escribir sobre ese buffer, luego cuando podamos leemos, pero leemos del buffer!!
Igual configuramos para interrupcion del TX, y esta ves en ves de esperar que se vacie, simplemente ponemos lo que queremos enviar en nuestro buffer, y de alli disparamos la interrupcion. Va a entrar a la interrupcion cada ves que envie un caracter, y dentro de la interrupcion parara, cuando este todo enviado, si mientras esta en medio de un mensaje, agregas al buffer mas datos, no va a existir problemas.
« Última modificación: 30 de Septiembre de 2017, 20:38:19 por KILLERJC »

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #8 en: 03 de Octubre de 2017, 14:37:56 »
Gracias por las recomendaciones KILLERJC.

Entonces para el ASM en C me quedo que no es necesario a menos que sea una aplicación muy critica en tiempo y ejecución.

Con respecto al ejemplo que propuse me quedo con que el compilador realizaría el trabajo de ingresar un nop si el delay fuera de 1u. A veces pienso que utilizar una función como el delay debe ocupar mas  espacio de memoria que setaer por uno mismo el timer que genera el retraso (como lo haría en ASM).

Con lo de la interrupción tenia claro que no debo poner en delays dentro de ella, en el ejemplo que postee era para que el ISI me diera el tiempo de ingresar al debbug cuando enviaba el string por el hyperterminal.

Ahora con respecto a las funciones estándar de C, agregue la libreria stdio.h y en mi programa escribí la funcion scanf(), al ingresar a la libreria stdio.h note que dice esto scanf() is not supported by this compiler, entonces esa función no se puede utilizar en este compilador (osea tenias razón).
Para hacer un"scanf" debiera hacer un  "scanf" que es el programa putchar() que haz escrito, solamente que la variable que se ingresa debiera ser un puntero (ono), algo asi
Código: C
  1. void putch(char* c)
  2. {
  3.     while(!TXSTAbits.TRMT);
  4.     TXREG = c;
  5. }
Ademas también note que esta el printf() y el compilador si la soporta. En mi programa agregue una linea de código para usar el printf (queria solo experimentar y saber que ocurría), hice un printf("Hola Mundo") compila y todo pero no hace nada ( ni por consola muestra el Hola Mundo). En si no entiendo bien para que seria útil esa función ya que cuando uno aprende el C de PC (por decirlo de algún modo) usaba esa función para interactuar.

Con respecto a las variable para los buffer para el Tx y el Rx tienes razón que se debe trabajar con ellas. Cuando hablas de "circulares" quieres decir que su tamaño es fijo??. El ejemplo que tu me diste seria un buffer de 64 bytes.
En mi programa quiero usar un buffer sin tamaño definido  ya que no se el tamaño del String a captar. Entonces debería tener algo asi:
Código: C
  1. volatile char bufferTX[];
  2.  
  3. void interrupt RX_interrupcion(void){  
  4.     unsigned int i = 0;
  5.     if(bufferTX[i] != '\0'){                            //  Deja de almacenar hasta que llegue el fin del string
  6.       bufferTX[i] = RCREG;
  7.       i++;
  8.     }
  9.     else{
  10.      i = 0;                    // para volver a almacenar de nuevo
  11.     ........................... // Aqui debiaera ir una linea que me avisara que llego un string
  12.     ........................... // aqui limpiar mi buffer, no se si sirva usar fflush()
  13.     }
  14. }

Ahora no se si tiene sentido usar la función Malloc(), Relloc() y Free() para almacenar un vector sin tamaño definido.

Gracias por comentar KillerJC

Saludos


 

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #9 en: 03 de Octubre de 2017, 16:49:49 »
El printf lo que hace es usar la funcion putch() para enviar caracter por caracter a donde lo uses, en realidad printf formatea el texto y le da un caracter a putch para que este lo envie.
Dentro XC8 posee una funcion putch (weak), pero que esta vacia, no hace nada. La idea es que vos realices tu propio putch para sacarlo por donde desees.

Ejemplo de putch para la USART:

Código: C
  1. void putch(char data) {
  2.     while( !TXSTAbits.TRMT);
  3.     TXREG = data; // send data
  4. }

Ejemplo de putch para la SPI:

Código: C
  1. void putch(char data) {
  2.     while( !PIR1bits.SSPIF);
  3.     SSPBUF = data; // send data
  4. }

Existe la forma de que el printf escriba en la consola, cuando simulas en el MPLAB X, yo no se como se hace, pero se que existe.

Citar
Cuando hablas de "circulares" quieres decir que su tamaño es fijo??.

https://es.wikipedia.org/wiki/Buffer_circular

Podes saber cuando esta lleno, no tenes que mover los datos hace al comienzo, etc.

Citar
En mi programa quiero usar un buffer sin tamaño definido

Imposible por la parte de "sin tamaño definido". Y las funciones malloc/free que usan el Heap estan prohibidas xD jeje, la memoria dinamica es muy costosa (computacionalmente) para los microcontroladores, por eso no se usan.

Asi que vas a tener que crear un buffer, lo suficientemente decente como para que pueda tener todos los datos hasta que los leas. que si esta bien realizado el programa (que lo lea en un tiempo considerable), esto no deberia ser problema.

PD:
Encontre un link sobre como imprimir a la "consola" cuando simules tu programa en el MPLAB X
http://microchipdeveloper.com/xc8:console-printing

Desconectado JuanjoPic

  • PIC12
  • **
  • Mensajes: 97
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #10 en: 03 de Octubre de 2017, 17:37:01 »
Gracias por la info KILLERJC.

Entonces se podría usar el printf para jugar un rato por consola jajjaj. Ademas note que la libreria stdio.h permite usar la función gets() en el XC8, entonces "creo" que se podría usar para recivir caracteres y así probar el bufferRX, y eso es algo que estaba buscando por que estaba usando el hyperterminal para enviar y recivir caracteres, pero no he encontrado una forma de enviar por hyperterminal un string..... osea escribir "Hola Mundo" + ENTER y asi el hyperterminal envie el string. Luego con el pic podria detectar el '\0' que determinar el termino de string.
El programa que he posteado anteriormente, que lo re-posteare, necesita de un string ya que los caracteres no llevan consigo un '\0' para saber que han terminado.

Código: C
  1. volatile char bufferTX[64];
  2.  
  3. void interrupt RX_interrupcion(void){  
  4.     unsigned char i = 0;
  5.     if(bufferTX[i] != '\0'){                            //  Deja de almacenar hasta que llegue el fin del string
  6.       bufferTX[i] = RCREG;
  7.       i++;
  8.     }
  9.     else{
  10.      i = 0;                    // para volver a almacenar de nuevo
  11.     ........................... // Aqui debiaera ir una linea que me avisara que llego un string
  12.     ........................... // aqui limpiar mi buffer, no se si sirva usar fflush()
  13.     }
  14. }

El programita almacena en el buffer hasta que lleve el '\0', una vez que ha llegado debiera darme un aviso ...se me ocurre usar una variable como bandera para decir que la info ya llego y puedo utilizar la información almacenada (ademas limpiaria el buffer creado).

Desconectado KILLERJC

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 8242
Re:Ayuda comunicación I2C PIC18F252
« Respuesta #11 en: 03 de Octubre de 2017, 22:24:10 »
No se como funciona el gets, no te puedo ser de ayuda en ese caso.

Lo que envíes con el cualquier terminal no va a tener el '\0' porque eso es interno a C. Usualmente lo que buscas son los saltos de lineas. Y aca es donde todo "depende"...

Windows usa '\r''\n', Linux '\n' , y tengo entendido que MAC '\r', asi que simplemente cuando detectas \r o \n y tu indice i no es 0, debes completarlo con '\0' y avisar que llego el string. Ademas reiniciar el tu indice i, a 0.

Los mayores problemas o bugs de C que existen, son por no tener un control sobre el indice de los arrays. Asi que si tu array es de 64, deberias tener si o si instrucciones que hagan algo en caso de que i sea 64 o mas, ejemplo volver el indice a 0.


 

anything