Hola amigos, aqui les pongo un programita de un pic maestro, que trabaja con 4 esclavos en forma serial, asi que hay 4 puertos virtuales y uno que va con la PC con rs485, es parte de miproyecto de tesis, y me ayudo a hacerlo un Ingeriero amigo, pero me gustaria saber como se le puede mejorar, o que den consejos, y para que vean todo lo que se puede hacer con CCS, hasta tiene como les he escrito para separar un pin y conectarlo con el 485, el protocolo implementado es con codigos ascii, es usado en algunos dispositivos, bueno se los dejo y que el foro siga creciendo
/*--------------------------------------------------------------------------------------
Funciones para recepcionar o enviar tramas
--------------------------------------------------------------------------------------
----------------------------
TRAMAS MAESTRO / ESCLAVOS:
----------------------------
Trama de transmision a los canales RS232-A a RS232-C:
*------------------------------------*
|SOH|STX|ENQ|RS|D1|D0|ETX|CHECKSUM|CR|
*------------------------------------*
Trama de recepcion de los RS232-A a RS232-C. Es la misma para la PC. La diferencia es
que los PIC D1 y D0 van de 00 a 16. En el caso de PC es de 00 a 47.
*--------------------------------------*
|SOH|STX|D1|D0|RS|C|D|U|ETX|CHECKSUM|CR|
*--------------------------------------*
Trama de comando que envia la el maestro a un esclavo indicando un PUERTO, una LINEA y
el valor que puede tener la misma (0 ó 1)
*-----------------------------------------------------*
|SOH|STX|DC1|RS|PORT|RS|LINEA|RS|VALOR|ETX|CHECKSUM|CR|
*-----------------------------------------------------*
----------------------
TRAMAS PC / MAESTRO:
----------------------
Trama que envia la PC para solicitar algun dato del buffer de los esclavos. Similar
al comando que se emplea para solicitar los datos a los esclavos desde el maestro.
*------------------------------------*
|SOH|STX|ENQ|RS|D1|D0|ETX|CHECKSUM|CR|
*------------------------------------*
Trama de comando que envia la PC al maestro indicando en el campo CANAL el destinatario
*--------------------------------------------------------*
|SOH|STX|DC1|CANAL|PORT|RS|LINEA|RS|VALOR|ETX|CHECKSUM|CR|
*--------------------------------------------------------*
Caracteres de control. Tabla de codigos ASCII
SOH - Comienzo de encabezamiento
STX - Comienzo de texto
D1 - Indicador de decenas del buffer
D0 - Indicador de unnidades del buffer
ETX - Final de texto
ENQ - Pregunta
Checksum - Suma en 8 bits de los valores anteriores
CR - Retorno de carro
RS - Separador de registros
C - Centenas
D - Decenas
U - Unidades
CANAL- Canal al cual se envia un comando desde la PC
/*------------------------------------------------------------------------------------
Definiciones y declaraciones
-----------------------------------------------------------------------------------*/
#define SOH 0x01 // Definicion de los caracteres. Tabla de codigo ASCII
#define STX 0x02
#define ETX 0x03
#define CR 0x0D
#define LF 0x0A
#define RS 0x1E
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define ENQ 0x05
#define DC1 0x11
//-------------------------------------------------------------------------------------
#define MAX_BUFFER 10 // Buffer para los datos de los esclavos
static char buffer[MAX_BUFFER*3];
//-------------------------------------------------------------------------------------
#define MAX_RX 11 // Buffer de recepcion. 11 posiciones
static char buffer_rx[MAX_RX];
static int ptro_buffer_rx=0; // Ptro a la sgte posicion que esta vacia
//-------------------------------------------------------------------------------------
#define MAX_PREGUNTA 9 // Nro caracteres de la pregunta de la PC
#define MAX_CONTROL1 12 // Nro caracteres del comando a un esclavo
#define MAX_USART 12 // Buffer para Rx del Usart
static char buffer_usart[MAX_USART];
static int ptro_buffer_usart=0; // Ptro a la sgte posicion que esta vacia
//-------------------------------------------------------------------------------------
char truefalse;
#bit flag =truefalse.0 // Bandera de tx/rx en canales
#bit tx_comando =truefalse.1 // Indica si hay comando disponible en un canal
#bit timeout_error =truefalse.2 // timeout en la lectura de un canal
char canal_comando; // Indica a que canal se va a eniar un comando
//-------------------------------------------------------------------------------------
#bit BIT_RS232A=PORTD.7 // Led indicador de actividad en el canal: RS232-A
#bit BIT_RS232B=PORTD.6 // RS232-B
#bit BIT_RS232C=PORTD.5 // RS232-C
#bit BIT_RS232D=PORTD.4 // RS232-D, canal para transmitir los valores a la PC
/*-------------------------------------------------------------------------------------
INTERRUPCION SERIAL
-------------------------------------------------------------------------------------*/
#byte SPBRG_C=SPBRG // Definicion de los registros que controlan el USART
#byte TXREG_C=TXREG
#byte RCREG_C=RCREG
#bit BRGH_C =TXSTA.BRGH // Definicion de los bits. Registros Tx/Rx
#bit SYNC_C =TXSTA.SYNC
#bit TXEN_C =TXSTA.TXEN
#bit TX9_C =TXSTA.TX9
#bit RX9_C =RCSTA.RX9
#bit CREN_C =RCSTA.CREN
#bit SPEN_C =RCSTA.SPEN
#bit TXIF_C =PIR1.TXIF // Definicion de flag de Tx/Rx
#bit RCIF_C =PIR1.RCIF
#bit OERR_C =RCSTA.OERR // Definicion de los bits de estado del USART en Rx
#bit FERR_C =RCSTA.FERR
/*-------------------------------------------------------------------------------------
FUNCIONES
-------------------------------------------------------------------------------------*/
// Rutina para la recepcion serial por interrupcion
static int dir_int; // Convierte de ASCII a Nro. Dir de buffer a enviar
static char centenas_int; // Centenas, decenas y unidades del dato a Tx
static char decenas_int;
static char unidades_int;
#int_rda
rda_isr()
{
// Recepcion de valores via interrupcion que son almacenados en buffer_usart
if(ptro_buffer_usart < MAX_USART){
if(!OERR_C && !FERR_C ){ // Revisa los bits de errores
if( RCREG_C == EOT) ptro_buffer_usart=0;
else
buffer_usart[ptro_buffer_usart++]=RCREG_C;
}
else{ // Control de errores en USART
CREN_C = 0; // Reset OERR
CREN_C = 1;
dir_int = RCREG_C; // Reset del FERR
ptro_buffer_usart=0; // Reset del buffer del usart
}
}
else
ptro_buffer_usart=0;
// Verifica si tiene un mensaje de la PC tipo pregunta
if(buffer_usart[2] == ENQ && ptro_buffer_usart == MAX_PREGUNTA){
// Verifica checksum de la trama
if(buffer_usart[0]+buffer_usart[1]+buffer_usart[2]+buffer_usart[3]+
buffer_usart[4]+buffer_usart[5]+buffer_usart[6]==buffer_usart[7]){
dir_int=( buffer_usart[4]-"0" ) * 10 + ( buffer_usart[5]-"0" );
// Pasa a texto el contenido de buffer[dir]
centenas_int = (buffer[dir_int] / 100)+"0";
decenas_int = buffer[dir_int] % 100;
unidades_int = (decenas_int % 10) +"0";
decenas_int = (decenas_int / 10) +"0";
// Envio del mensaje a la PC
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7,ENABLE=PIN_C5)
putc(SOH) ;delay_ms(2);
putc(STX) ;delay_ms(2);
printf("%02u",dir_int) ;delay_ms(2);
putc(RS) ;delay_ms(2);
printf("%03u",buffer[dir_int]) ;delay_ms(2);
putc(ETX) ;delay_ms(2);
putc(SOH+STX+buffer_usart[4]+
buffer_usart[5]+
RS+centenas_int+decenas_int+
unidades_int+ETX)
;delay_ms(2);
putc(CR); ;delay_ms(2);
// Hace cambio en el led indicador
BIT_RS232D=0;delay_ms(100);BIT_RS232D=1;delay_ms(100);BIT_RS232D=0;
}
ptro_buffer_usart=0; // Reset del buffer del USART
}
// Verifica si tiene un mensaje de la PC tipo pregunta
if(buffer_usart[2] == DC1 && ptro_buffer_usart == MAX_CONTROL1){
// Verifica checksum de llegada
if(buffer_usart[0]+buffer_usart[1]+buffer_usart[2]+buffer_usart[3]+
buffer_usart[4]+buffer_usart[5]+buffer_usart[6]+buffer_usart[7]+
buffer_usart[8]+buffer_usart[9]==buffer_usart[10]){
if(buffer_usart[3]=="A") canal_comando=1; // Elije el canal
if(buffer_usart[3]=="B") canal_comando=2;
if(buffer_usart[3]=="C") canal_comando=3;
buffer_usart[3]=RS; // Prepara trama que va a enviar al canal
buffer_usart[10]=buffer_usart[0]+buffer_usart[1]+buffer_usart[2]+
buffer_usart[3]+buffer_usart[4]+buffer_usart[5]+
buffer_usart[6]+buffer_usart[7]+buffer_usart[8]+
buffer_usart[9];
tx_comando=TRUE; // Indica en el bucle principal que hay
// mensaje. Esto para evitar colisiones
// en los accesos concurrentes
// Hace cambio en el led indicador
BIT_RS232D=0;delay_ms(100);BIT_RS232D=1;delay_ms(100);BIT_RS232D=0;
}
ptro_buffer_usart=0; // Reset del buffer del USART
}
}
void configurar_usart()
{
BRGH_C = 1; // 9600 para un XTAL de 4MHz
SPBRG_C = 25;
SYNC_C = 0; // Configuracion del TXSTA y RCSTA
SPEN_C = 1; // Habilita pines de salida del USART
TX9_C = 0; // 8 bits de datos en Tx
TXEN_C = 1; // Habilita la transmision
RX9_C = 0; // 8 bits de datos en Rx
CREN_C = 1; // Recepcion continua
}
/*-------------------------------------------------------------------------------------
FUNCIONES
-------------------------------------------------------------------------------------*/
static int canal=0,posicion=0;
void valida_rs232()
{
if(ptro_buffer_rx >= MAX_RX){ // Si hay mensaje completo
// Calculamos el checksum de los datos que han llegado
if(buffer_rx[0]+buffer_rx[1]+buffer_rx[2]+
buffer_rx[3]+buffer_rx[4]+buffer_rx[5]+
buffer_rx[6]+buffer_rx[7]+buffer_rx[8]==buffer_rx[9])
buffer[canal*MAX_BUFFER+posicion]=(buffer_rx[5]-"0")*100+
(buffer_rx[6]-"0")*10+buffer_rx[7]-"0";
}
}
static unsigned long timeout; // Varibles para generar el timeout de un mensaje
int solicita_dato()
{
ptro_buffer_rx=0; // Inicializa valores y calcula checksum
timeout_error=FALSE;
timeout=0;
if(canal==0){
#use rs232(baud=9600,xmit=PIN_B3,rcv=PIN_B2) // RS232-A
BIT_RS232A=0; // Led que indica Tx
putc(EOT); delay_ms(2);
putc(SOH); delay_ms(2);
putc(STX); delay_ms(2);
putc(ENQ); delay_ms(2);
putc(RS); delay_ms(2);
putc(posicion / 10+"0");delay_ms(2);
putc(posicion % 10+"0");delay_ms(2);
putc(ETX); delay_ms(2);
putc(SOH+STX+ENQ+RS+ (posicion / 10+"0") + (posicion % 10+"0") +ETX);
delay_ms(2);
putc(CR);
do{
while(!kbhit()&&(++timeout<50000)) delay_us(10);// Espera rpta con timeout
if(kbhit()) buffer_rx[ptro_buffer_rx++]=getc();
else timeout_error=TRUE;
if(ptro_buffer_rx >= MAX_RX || timeout_error == TRUE)break;
}while(1);
if(timeout_error == FALSE) BIT_RS232A=1; // Led que indica Tx
}
if(canal==1){
#use rs232(baud=9600,xmit=PIN_B4,rcv=PIN_B5) // RS232-B
BIT_RS232B=0;
putc(EOT); delay_ms(2);
putc(SOH); delay_ms(2);
putc(STX); delay_ms(2);
putc(ENQ); delay_ms(2);
putc(RS); delay_ms(2);
putc(posicion / 10+"0");delay_ms(2);
putc(posicion % 10+"0");delay_ms(2);
putc(ETX); delay_ms(2);
putc(SOH+STX+ENQ+RS+ (posicion / 10+"0") + (posicion % 10+"0") +ETX);
delay_ms(2);
putc(CR);
do{
while(!kbhit()&&(++timeout<50000))delay_us(10); // Espera rpta con timeout
if(kbhit()) buffer_rx[ptro_buffer_rx++]=getc();
else timeout_error=TRUE;
if(ptro_buffer_rx >= MAX_RX || timeout_error == TRUE)break;
}while(1);
if(timeout_error == FALSE) BIT_RS232B=1;
}
if(canal==2){
#use rs232(baud=9600,xmit=PIN_D3,rcv=PIN_D2) // RS323-C
BIT_RS232C=0;
putc(EOT); delay_ms(2);
putc(SOH); delay_ms(2);
putc(STX); delay_ms(2);
putc(ENQ); delay_ms(2);
putc(RS); delay_ms(2);
putc(posicion / 10+"0");delay_ms(2);
putc(posicion % 10+"0");delay_ms(2);
putc(ETX); delay_ms(2);
putc(SOH+STX+ENQ+RS+ (posicion / 10+"0") + (posicion % 10+"0") +ETX);
delay_ms(2);
putc(CR);
do{
while(!kbhit()&&(++timeout<50000))delay_us(10); // Espera rpta con timeout
if(kbhit()) buffer_rx[ptro_buffer_rx++]=getc();
else timeout_error=TRUE;
if(ptro_buffer_rx >= MAX_RX || timeout_error == TRUE)break;
}while(1);
if(timeout_error == FALSE) BIT_RS232C=1;
}
if(timeout_error == TRUE || ptro_buffer_rx < MAX_RX) return 1;
delay_ms(10);
return 0;
}
/*--------------------------------------------------------------------------------------
Fin de la libreria
--------------------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------------------
Funcion principal
--------------------------------------------------------------------------------------*/
#zero_ram
void main()
{
set_tris_a(0x00); // Configura los puertos
set_tris_b(0x24);
set_tris_c(0x80);
set_tris_d(0x04);
set_tris_e(0x00);
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7,ENABLE=PIN_C5)
putc(SOH);
delay_ms(2); // Envia un caracter para indicar que el equipo esta operativo
configurar_usart(); // Configura el usart
enable_interrupts(INT_RDA); // Habilita la interrupcion del Rx Serial
enable_interrupts(global); // Habilita el GIE
tx_comando=FALSE;
do{
// Lectura de los valores que estan en los esclavos
flag=FALSE;
for(posicion=0;posicion<MAX_BUFFER;posicion++){
flag = solicita_dato();
if(flag==FALSE) valida_rs232();
else
for(posicion=0;posicion<MAX_BUFFER;posicion++)
buffer[canal*MAX_BUFFER+posicion]=0x00;
}
canal++; // Representa el cambio de canal que leemos (0,1,2)
if(canal==3) canal=0;
if(tx_comando==TRUE){ // Si hay comando para transmitir
if(canal_comando==1){
#use rs232(baud=9600,xmit=PIN_B3,rcv=PIN_B2) // RS232-A
putc(EOT);
delay_ms(2);
for(posicion=0;posicion<MAX_CONTROL1;posicion++){
putc(buffer_usart[posicion]);
delay_ms(2);
}
putc(EOT);
delay_ms(2);
}
if(canal_comando==2){
#use rs232(baud=9600,xmit=PIN_B4,rcv=PIN_B5) // RS232-B
putc(EOT);
delay_ms(2);
for(posicion=0;posicion<MAX_CONTROL1;posicion++){
putc(buffer_usart[posicion]);
delay_ms(2);
}
putc(EOT);
delay_ms(2);
}
if(canal_comando==3){
#use rs232(baud=9600,xmit=PIN_D3,rcv=PIN_D2) // RS323-C
putc(EOT);
delay_ms(2);
for(posicion=0;posicion<MAX_CONTROL1;posicion++){
putc(buffer_usart[posicion]);
delay_ms(2);
}
putc(EOT);
delay_ms(2);
}
tx_comando=FALSE; // Resetea bandera de transmision de comando
}
}while(TRUE);
}