TODOPIC
Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: pocher en 11 de Noviembre de 2007, 06:35:50
-
Sigo repasando cosas que con el tiempo se me olvidaron. Llevo día y medio con esto y no me sale, a ver si me podeis hechar una mano.
Simplemente se trata de provar la comunicación vía RS485 con 2 PICs. El PIC MASTER envia 1 byte y el PIC SLAVE lo recoge y lo saca por el portb.
Os pongo los dos programas, el del MASTER y el del SLAVE:
El del MASTER:
#include <16f876.h>
#fuses XT, NOPROTECT, NOWRT, PUT, NOWDT, NOLVP, NOCPD, BROWNOUT
#use delay(clock=4000000)
#use rs232(baud=1200, xmit=pin_c6, rcv=pin_c7)
#use fast_io(B)
void main(void)
{
char a[9];
char b[9];
int n;
a[0]='1';
a[1]='1';
a[2]='1';
a[3]='1';
a[4]='0';
a[5]='0';
a[6]='0';
a[7]='0';
b[0]='0';
b[1]='0';
b[2]='0';
b[3]='0';
b[4]='1';
b[5]='1';
b[6]='1';
b[7]='1';
output_high(pin_c5); //Habilitamos la transmisión de datos
while(1)
{
if (input(pin_b0))
{
for(n=0;n<8;n++)
putc(a[n]);
}
else
{
for(n=0;n<8;n++)
putc(b[n]);
}
delay_ms(1000);
}
}
El del SLAVE:
#include <16f876.h>
#fuses XT, NOPROTECT, NOWRT, PUT, NOWDT, NOLVP, NOCPD, BROWNOUT
#use delay(clock=4000000)
#use rs232(baud=1200, xmit=pin_c6, rcv=pin_c7)
#use fast_io(B)
char a[9];
int j=0;
#int_rda
void recepcion_serie() // Rutina de interrupción por recepción serie
{
if(kbhit())
{
a[j] = getc();
j++;
}
}
void main(void)
{
int i;
set_tris_b(0x00); //Puerto B-> salidas
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
output_low(pin_c5); //Habilitamos la recepción de datos
output_b(0x00);
//for(i=0;i<8;i++) //Sin inicializar a 0 también va FÍSICAMENTE
//a[i]=0;
while(1)
{
//if(a[0]=='1' && j==8) //Así no funciona ¿porqué?
if(j==8) //Con el IF es mejor que con el WHILE ya que el programa puede hacer otras cosas
//while( j < 8 ); //Con el WHILE tiene el inconveniente que hay que esperar la llegada de los bytes
{
disable_interrupts(INT_RDA);
j=0;
output_bit(PIN_B0,bit_test(a[0],0)); // Se pregunta si ¿el primer bit del byte a[0], es 1 ó 0?
output_bit(PIN_B1,bit_test(a[1],0));
output_bit(PIN_B2,bit_test(a[2],0));
output_bit(PIN_B3,bit_test(a[3],0));
output_bit(PIN_B4,bit_test(a[4],0));
output_bit(PIN_B5,bit_test(a[5],0));
output_bit(PIN_B6,bit_test(a[6],0));
output_bit(PIN_B7,bit_test(a[7],0));
//for(i=0;i<8;i++) //Sin inicializar a 0 también va FÍSICAMENTE
//a[i]=0;
enable_interrupts(INT_RDA);
}
}
}
El caso es que si en el SLAVE pongo if(j==8) sí que lograr sacar el byte por el portb, pero si pongo if(a[0]=='1' && j==8) ya no saca nada, y a mí me interesa esta segunda opción para poder aplicarla a otro programa con más PICs.
Teóricamente debería de sacar 00001111 por el portb del SLAVE cuando RB0=1 en el PIC emisor, pero no ... quiere, si metemos el a[0]=='1' ya no va.
Un saludo
PD. Os dejo los ficheros .dsn, .c y .hex para que lo comproveis. Físicamente responde igual que con PROTEUS
¡No deja adjuntar ningun archivo por pequeño que sea, lo siento!
Os los dejo aquí: DESCARGAR (http://rapidshare.com/files/68931581/RS485.rar)
-
Amigo Pocher, dale un vistazo a este trabajo que hice por si te sirve de algo: El RS485, un Relé en la lejanía: Hardware y Software (http://www.todopic.com.ar/foros/index.php?topic=19057.0)
-
Seguro que sí, amigo Diego voy a llevar a los niños a la feria y esta tarde me lo miro.
Un saludo
-
Hola pocher.
Es posible que que algún duendecillo haya modificado en el slave esto?
output_low(pin_c5);
Para habilitar la comunicación en el emisor lo pones en high, no tendría que ser igual?
Un saludo desde Alicante.
-
Deberia ser igual una habilitacion que otra, NO?? :lol:
-
Para habilitar la transmisión del 485 Master, el Enable de transmisión debe de estar en alto (habilitado) y el de recepción también, y así este último estará bloqueado (va por ceros).
Para habilitar la recepción del 485 Slave, el Enable de recepción debe de estar en bajo (habilitado) y el de transmisión también para que esté bloqueado (va por unos).
Se pueden habilitar dos pines del PIC para el control de los Enables o bien como hay en el programa cortocircuitar ambos Enables y así solo usar un pin del PIC.
PD. Llego ahora de la calle, voy a mirarme lo de Diego.
-
Yo tengo los dos enables conectados de forma con con uno solo pin controlo ambos, así en HIHG habilito la transmisión y en LOW la recepción. :mrgreen:
-
... Es posible que que algún duendecillo haya modificado en el slave esto? ....
ya vas.. :x
-
Diego he intentado copiar y pegar el código GeSHi (ccp) y me aparecen todos los números de línea del GeSHi y caracteres raros ... un desastre. Podrías ponerlo en otra forma que no dé problemas al copiar?
-
Aqui te lo pego en texto plano. :mrgreen:
/** \file firmware_iRELE485-628.c
* \brief Este fichero contiene el cuerpo principal del Firmware del\n
* dispositivo auxiliar satélite de la iACD V1.0.4. denominado iRELE485-628
*
* Integra todos los subsistemas y funciones implementados.\n
* Source para el compilador CCS C v.3.242
*
* Microprocesador : 16F628A (18 pines PDIP) \n\n
* Fuses utilizados: \n
* \li INTRC Internal RC Osc
* \li MCLR Master Clear pin enabled
* \li NOWDT No Watch Dog Timer
* \li NOPROTECT Code not protected from reading
* \li NOPUT No Power Up Timer
* \li NOBROWNOUT No brownout reset
* \li NOLVP No low voltage prgming, B3(PIC16)
* \li NOCPD No EE protection
*
* \author Diego Márquez García-Cuervo \n
* <http://picmania.garcia-cuervo.net>
*
* \version 1.0.0.A
*/
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Definiciones estándar del 16F628A
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <16F628A.h>
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Fuses y ajuste de Clock
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#fuses INTRC,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOLVP,NOCPD
#use delay(clock=4000000)
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Defines y Constantes
//
///////////////////////////////////////////////////////////////////////////////////////////////////
/*! \def DRIVER_RELE
* Pin accionador del Relé número 1 \li HIGH : Relé ON.
*/
#define DRIVER_RELE PIN_B4
/*! \def TX_485_ENABLE
* Pin que habilita la transmisión por el canal RS485
* \li LOW Habilita recepción RS485
* \li HIGH Habilita transmisión RS485.
*/
#define TX_485_ENABLE PIN_B5
/*! \def bytes_for_USART_buffer
* \brief Establece el número de bytes de longitud del buffer de la USART.
*/
#define bytes_for_USART_buffer 12
/*! \var const char VERSION
* \brief Versión del Firmware.
*/
const char VERSION[]="1.0.0\0";
/*! \var const char WhatAmI
* \brief Identificador de tipo de dispositivo: 'R' Relé.
*/
const char WhatAmI = 'R';
/*! \var const char WhoAmI
* \brief Identificador de dispositivo.
*/
const char WhoAmI = '1';
/*! \var const char Command_Execute
* \brief Comando que indica ejecución de acción : Activar Relé
*/
const char Command_Execute = 'X';
/*! \var const char end_of_transmit
* \brief Carácter que indica fin de transmisión (no buffereable)
*/
const char end_of_transmit = '!';
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Canal de Comunicación
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#use rs232(baud=9600,xmit=PIN_B2,rcv=PIN_B1)
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Prototipos de Funciones
//
///////////////////////////////////////////////////////////////////////////////////////////////////
void USART_activa_tx(void);
void USART_activa_rx(void);
void USART_add_to_buffer(char c);
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// R A M : Variables Globales Estáticas
//
///////////////////////////////////////////////////////////////////////////////////////////////////
/*! \var USART_buffer
* \brief Buffer de la USART
*/
static char USART_buffer[bytes_for_USART_buffer];
/*! \var USART_nextRec
* \brief Indice del Buffer de la USART
*/
static int8 USART_nextRec;
/** \var Command
* \brief Comando válido para ejecutar.
*/
int8 Command;
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Funciones
//
///////////////////////////////////////////////////////////////////////////////////////////////////
/** \brief Abre el canal RS485 para transmitir.
* \return void
*/
void USART_activa_tx(void){
output_high(TX_485_ENABLE);
delay_ms(5);
}
/** \brief Abre el canal RS485 para recibir.
* \return void
*/
void USART_activa_rx(void){
delay_ms(5);
output_low(TX_485_ENABLE);
delay_ms(1);
}
/** \brief Inicializa el Buffer de la USART
*
* Recoge el último carácter recibido desde la USART sobre USART_buffer
* e incrementa en 1 el índice USART_nextRec.\n\n
* Si el carácter recibido es Retorno de carro '\\r' ó 0x0D comprueba identidad de dispositivo y comando recibido
*
* \return void
*/
void USART_add_to_buffer(char c){
USART_buffer[USART_nextRec++]=c;
if(USART_nextRec==bytes_for_USART_buffer){
USART_nextRec=0;
}
if(c==end_of_transmit){
--USART_nextRec;
if(USART_buffer[USART_nextRec-3]==WhatAmI){
if(USART_buffer[USART_nextRec-2]==WhoAmI){
if(USART_buffer[USART_nextRec-1]==Command_Execute){
Command=0x01;
}
}
}
USART_nextRec = 0;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// Rutinas de Servicio de Interrupciones
//
///////////////////////////////////////////////////////////////////////////////////////////////////
#int_rda
/** \brief Interrupción por : Recepción del Canal Serie.
*
*/
void interrupt_service_rutine_rda(void) {
char USART_nextChar;
USART_nextChar='\0';
if(kbhit()){
USART_nextChar=getc();
USART_add_to_buffer(USART_nextChar);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// M A I N
//
///////////////////////////////////////////////////////////////////////////////////////////////////
/** \brief Constituye el núcleo principal del sistema
*
* \return void.
*/
void main(void) {
setup_oscillator(OSC_4MHZ);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
setup_timer_1(T1_INTERNAL|T1_DIV_BY_4);
setup_timer_2(T2_DISABLED,0,1);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
port_b_pullups(False);
set_tris_b(0b00000010);
output_low(DRIVER_RELE);
delay_ms(100);
USART_nextRec = 0;
Command = 0x00;
USART_activa_rx();
delay_ms(333);
enable_interrupts(global);
enable_interrupts(int_rda);
USART_activa_tx();
printf("[iRELE485-628] v.%s\r\n",version);
USART_activa_rx();
do{
if(Command==0x01){
Command =0x00;
USART_activa_tx();
printf("R%cA%c\r\n",WhoAmI,end_of_transmit);
USART_activa_rx();
output_high(DRIVER_RELE);
delay_ms(2000);
output_low(DRIVER_RELE);
}
} while(true);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
//
// End of firmware
//
///////////////////////////////////////////////////////////////////////////////////////////////////
-
Diego he intentado copiar y pegar el código GeSHi (ccp) y me aparecen todos los números de línea del GeSHi y caracteres raros ... un desastre. Podrías ponerlo en otra forma que no dé problemas al copiar?
Siendo Moderador del subforo tú mismo podías hacerlo :D
Basta con darle a "Modificar" para obtener lo que había escrito Diego.
-
Debe pasarle a Pocher lo que a mi me pasa...
Me cuesta tomar el POST de alguien y corregirlo, por una cuestión de respeto, entonces le escribimos para que el dueño del post tome acción... :mrgreen: :mrgreen:
-
Claro, a mí también me pasa. Pero si le das a "Modificar", puedes copiar el contenido del post sin darle a "Guardar" y no cambiarás nada.
Bah, sólo lo decía porque alguna vez me ha pasado lo del Geshi y lo he resuelto de esa forma.
-
Gracias Manolo, gracias Marcos, los dos demostrais tener muy buena voluntad.
Y como no gracias al caballero Don Diego que ha hecho que mis 3 PICs se comuniquen.
Diego te hago unas preguntas, a ver que te parece:
1ª ¿Para que sirve?:
if(USART_nextRec==bytes_for_USART_buffer){
USART_nextRec=0;}
Quizá sirva por si se desborda el Buffer, o por si no lee el byte de fin de transmisión (!) que se inicialize el índice.
2ª ¿Los delay_ms tienen que ser del orden de esos valores, para que no hayan problemas? He hecho una prueba cargandomelos y se comunican físicamente bien(y con PROTEUS también).
Seguiré investigando el porqué no funciona el programa que puse inicialmente, le debe de faltar alguna tontería.
En estos próximos días haré una prueba en la que los 3 PICs actuen tanto como Maestros como Esclavos todos a la vez, a ver si se les hace un nudo en la lengua a los condenaos, que me han traido frito.
De nuevo gracias Diego.
-
Diego te hago unas preguntas, a ver que te parece:
1ª ¿Para que sirve?:
if(USART_nextRec==bytes_for_USART_buffer){
USART_nextRec=0;}
Quizá sirva por si se desborda el Buffer, o por si no lee el byte de fin de transmisión (!) que se inicialize el índice.
Exacto
2ª ¿Los delay_ms tienen que ser del orden de esos valores, para que no hayan problemas? He hecho una prueba cargandomelos y se comunican físicamente bien(y con PROTEUS también).
Estos delays son para usar el conversor RS232-RS485 para conectarme al PC, es semiautomático y detecta cuando tiene que conmutar de recepción a transmisión o viceversa y requiere un tiempo para hacerlo. Si tus dispositivos están siempre en el modo correcto puedes eliminarlos y en paz
-
¡OK!
-
Un comentario respecto a los delay_ms
En el primer programa que me hice le quite los delay_ms y seguía funcionando, pero en este en el cual todos actuan como maestros y esclavos me ha sido necesario poner un delay_ms(10) (con menos tiempo seguramente también funcionaría) entre después de transmitir el último dato y antes de activar el pin de recepción.
Quizá por si acaso se deban meter delay_ms antes de activar los pines de transmisión o recepción de los RS485. Así vamos a lo seguro.
Un saludo
-
Si bien el RS485 admite varios Maestros, no es muy buena idea hacerlo, ya que colisionan las comunicaciones y pueden estar varios segundos sin comunicarse, como no tienes deteccion de colisiones solo te queda reenviar el mensaje en forma repetida.
Alli es donde necesitas los delays y especialmente que sean diferentes... :mrgreen: :mrgreen:
Si quieres un protocolo que detecte colisiones, pueda trabajar en ambientes multimaestros y sea bueno y efectivo... venite para el CAN !!! :D :D
-
bueno he visto con el Atmel que RS458 puede ser muchos Master siempre!
y digo yo no seria buenos usar interruptores para cada modulo en Cascada y que cada uno reconozca su posicion y esperando orden quien manda? :mrgreen:
esa seria un buena solucion!
:-/
-
En realidad si la topologia es Multimaster, cada uno deberia poder transmitir cuando deba hacerlo, lo que no se puede prever es cuando ocurre una colision, dependiendo cuanto de buenos son los cristales utilizados en los clocks de capa micro y de otras yerbas.
Puede darse el caso que nunca ocurra y tambien puede ser que ocurra con asiduidad...
Que pasa en este ultimo caso si el dato que uno de los master estaba por transmitir es muy importante para el sistema completo (por ejemplo una presion en una caldera), si ocurren colisiones se retrasara bastante el envio del mismo, que hacer en ese caso??