Este archivo define la configuración base de hardware para Protolink V2 sobre el PIC18F67K40, incluyendo:
- Mapeo de pines (botones, LEDs, UARTs, RS-485, optoentradas, salidas ULN2003, I2C, etc.).
- Configuración por defecto de puertos (
TRISx) e I/O. - Canal de debug serie estándar.
- Infraestructura opcional para conteo de pulsos por segundo usando:
- INT0 / INT1 (interrupciones externas).
- SMT1 / SMT2 (Signal Measurement Timer) como contadores de pulsos.
- Timer0 como generador de ventana aproximada de 1 segundo.
Todo esto se activa de manera centralizada, pensado para reutilizarse en todos los firmwares Protolink V2.
El archivo asume:
- MCU:
PIC18F67K40 - Fuses:
HS(oscilador de alta velocidad externo) - Configuración de reloj:
#include <18F67K40.h>
#device ADC = 10
#fuses HS
#use delay(crystal=16mhz, clock=64mhz)#define USER PIN_B2 // Interrupción 2
#define LED1 PIN_F0
#define LED2 PIN_F1USER: botón de usuario.
LED1, LED2: indicadores de estado.
#define TX_1 PIN_C6 // UART1 TX
#define RX_1 PIN_C7 // UART1 RX
#define ETH_RST PIN_D7UART1 se usa para el módulo Ethernet.
ETH_RST: salida de reset al módulo Ethernet.
#define TX_2 PIN_G1 // UART2 TX
#define RX_2 PIN_G2 // UART2 RX
#define TX_3 PIN_E0 // UART3 TX
#define RX_3 PIN_E1 // UART3 RXUARTs dedicados para puertos RS-232 en headers externos.
#define TX_4 PIN_C0 // UART4 TX
#define RX_4 PIN_C1 // UART4 RX
#define TX_ENABLE PIN_H0 // Enable driver RS485
#define RX_ENABLE PIN_H1 // Enable receiver RS485UART4 para bus RS-485.
TX_ENABLE / RX_ENABLE controlan el transceiver (dirección TX/RX).
#define TX_5 PIN_E2 // UART5 TX
#define RX_5 PIN_E3 // UART5 RXSe usa como puerto de debug principal.
#define EE_WP PIN_G7 // Write protect de EEPROM
#define RTC_INT PIN_B3 // Interrupción RTC
#define ADC_RDY PIN_F7 // Señal de alerta/ready de ADC (por implementar)#define PIN_SDA PIN_C4 // I2C SDA general
#define PIN_SCL PIN_C3 // I2C SCL general
#define GPIO_1 PIN_F2
#define GPIO_2 PIN_F3I2C1 está mapeado a PIN_SCL / PIN_SDA.
GPIO_1 y GPIO_2 libres para funciones varias.
#define OUT_0 PIN_D0
#define OUT_1 PIN_D1
#define OUT_2 PIN_D2
#define OUT_3 PIN_D3Salidas de colector abierto a través de ULN2003, típicamente para relevadores u otras cargas discretas.
#define IN_0 PIN_B0 // INT0
#define IN_1 PIN_B1 // INT1
#define IN_2 PIN_E5 // CCP1 & (SMT1SIG)
#define IN_3 PIN_E4 // CCP2 & T3CKI
#define IN_2_CCP PIN_E5
#define IN_3_CCP PIN_E4
#define IN_2_CKI PIN_A4
#define IN_3_CKI PIN_B5IN_0, IN_1: entradas opto con soporte de interrupciones externas INT0 / INT1.
IN_2, IN_3: entradas opto asociadas a CCP y fuentes de clock de timers, ideales para medición de pulsos/frecuencia (cuentalitros, etc.).
Aliases extra (IN_2_CCP, IN_3_CCP, IN_2_CKI, IN_3_CKI) permiten reutilización flexible según el periférico.
El archivo ya configura los periféricos clave:
// UARTs
#pin_select U1TX = TX_1
#pin_select U1RX = RX_1
#pin_select U2TX = TX_2
#pin_select U2RX = RX_2
#pin_select U3TX = TX_3
#pin_select U3RX = RX_3
#pin_select U4TX = TX_4
#pin_select U4RX = RX_4
#pin_select U5TX = TX_5
#pin_select U5RX = RX_5
// I2C1
#pin_select SCL1IN = PIN_SCL
#pin_select SDA1IN = PIN_SDA
// SMT
#pin_select SMT1SIG = PIN_E5 // SMT1 vinculado a IN_2
#pin_select SMT2SIG = PIN_G7 // SMT2 (compartido con EE_WP si se usa)
Nota: SMT1 está conectado a IN_2 (E5), lo que permite usar esa entrada opto como canal de conteo de pulsos por hardware vía SMT1.
#use rs232(baud=115200, parity=N, UART5, bits=8, stream=DEBUG, errors)
#define protolink_debug_msg(msg) fprintf(DEBUG, msg)
#define protolink_debug_data(msg, data) fprintf(DEBUG, msg, data)Debug por UART5 @ 115200 bps.
Macros para logging rápido:
protolink_debug_msg("texto\r\n");
protolink_debug_data("Valor: %lu\r\n", valor);void protolink_io_init(void)
{
// Puertos a "modo seguro" al inicio
set_tris_a(0xFF);
set_tris_b(0xFF);
// C: UART1, UART4, I2C
set_tris_c(0b10111110);
// D: salidas ULN2003 + ETH_RST
set_tris_d(0b01110000);
// E: UART3, UART5, entradas opto
set_tris_e(0b11111010);
// F: LEDs + GPIO + ADC_RDY
set_tris_f(0b11110000);
// G: UART2 + EEPROM WP
set_tris_g(0b01111101);
// H: control RS485
set_tris_h(0b11111100);
output_float(PIN_SDA);
output_float(PIN_SCL);
}Define la direccionalidad de todos los puertos de Protolink V2.
Deja I2C en estado “float” para permitir uso correcto de open-drain.
Si se define el macro:
#define PROTOLINK_DEFAULT
#include "protolink.h"se habilita un conjunto de funciones y variables globales orientadas a:
Generar una base de tiempo aproximada de 1 segundo usando Timer0.
Contar pulsos por segundo de:
INT0 / INT1 (entradas opto IN_0 e IN_1).
SMT1 / SMT2 como contadores de pulsos hardware.
Disponer de un helper tipo protolink_one_second() para lógica periódica.
Variables:
int16 NInts = 488; // Número de interrupciones de TMR0 para ~1 s
int16 C_Ints = 0; // Contador de interrupciones
int1 Flag = 0; // Flag que cambia cada ~1 s
int1 lastFlag = 0; // Estado anterior del flagCon la configuración utilizada:
Timer0 interno, 8 bits, prescaler 1:128.
Cada overflow de Timer0 ≈ 2.048 ms.
488 * 2.048 ms ≈ 999.4 ms (~1 s).
ISR de Timer0
#INT_TIMER0
void TIMER0_isr(void)
{
C_Ints++;
if (C_Ints >= NInts)
{
C_Ints = 0;
Flag = !Flag; // Toggle cada ~1 s
// Cálculo de pps INT0/INT1
...
// Cálculo de pps SMT1/SMT2
...
}
clear_interrupt(INT_TIMER0);
}Cada vez que se cumple el conteo de NInts, se:
Togglea Flag.
Actualizan los pulsos por segundo de INT0/INT1 y SMT1/SMT2.
Inicialización de Timer0
void protolink_timer0_init(void)
{
setup_timer_0(T0_INTERNAL | T0_DIV_128 | T0_8_BIT);
C_Ints = 0;
Flag = 0;
clear_interrupt(INT_TIMER0);
enable_interrupts(INT_TIMER0);
enable_interrupts(GLOBAL);
set_timer0(0);
}Variables:
volatile unsigned int32 flow1_counter = 0; // Conteo total INT0
volatile unsigned int32 flow2_counter = 0; // Conteo total INT1
volatile unsigned int32 flow1_pps = 0; // Pulsos/seg (INT0)
volatile unsigned int32 flow2_pps = 0; // Pulsos/seg (INT1)
ISRs:
#INT_EXT
void isr_int0(void) { flow1_counter++; }
#INT_EXT1
void isr_int1(void) { flow2_counter++; }En la ISR de Timer0, se calcula:
flow1_pps = c1 - last1;
flow2_pps = c2 - last2;donde c1/c2 son las cuentas actuales y last1/last2 las anteriores.
Inicialización de interrupciones externas
void protolink_interrupts_init(void)
{
ext_int_edge(0, L_TO_H); // INT0 flanco de subida
ext_int_edge(1, L_TO_H); // INT1 flanco de subida
clear_interrupt(INT_EXT);
clear_interrupt(INT_EXT1);
enable_interrupts(INT_EXT);
enable_interrupts(INT_EXT1);
}Variables:
volatile unsigned int32 flow1_smt_pps = 0; // Pulsos/seg medidos por SMT1
volatile unsigned int32 flow2_smt_pps = 0; // Pulsos/seg medidos por SMT2Inicialización de SMT:
void protolink_smt_init(void)
{
setup_smt1(
SMT_ENABLED
| SMT_TMR_ROLLSOVER
| SMT_SIG_ACTIVE_HIGH
| SMT_TMR_INCREMENTS_ON_RE
| SMT_DIV_BY_1
| SMT_REPEAT_DATA_ACQ_MODE
| SMT_MODE_COUNTER
| SMT_CLK_FOSC_DIV_4
| SMT_START_NOW
);
smt1_reset_timer();
setup_smt2(
SMT_ENABLED
| SMT_TMR_ROLLSOVER
| SMT_SIG_ACTIVE_HIGH
| SMT_TMR_INCREMENTS_ON_RE
| SMT_DIV_BY_1
| SMT_REPEAT_DATA_ACQ_MODE
| SMT_MODE_COUNTER
| SMT_CLK_FOSC_DIV_4
| SMT_START_NOW
);
smt2_reset_timer();
}En la ISR de Timer0, se leen los contadores:
unsigned int32 smt1_now = smt1_read(SMT_TMR_REG);
unsigned int32 smt2_now = smt2_read(SMT_TMR_REG);
flow1_smt_pps = smt1_now - smt1_last;
flow2_smt_pps = smt2_now - smt2_last;
smt1_last = smt1_now;
smt2_last = smt2_now;6.4. Helper de “evento cada segundo”
int1 protolink_one_second(void)
{
if (Flag != lastFlag)
{
lastFlag = Flag;
return true;
}
return false;
}Uso típico:
if (protolink_one_second())
{
// Aquí se ejecuta una vez por segundo aprox.
// Ej: imprimir pps, calcular caudal, etc.
}#define LOADER_END 0x5FF // ajuste de memoria para protolink v2 bootloaderDefine el final del área ocupada por el bootloader Protolink V2.
El firmware de aplicación debe respetar esta región (configuración de linker / #build).
Ejemplo mínimo de uso con defaults:
#define PROTOLINK_DEFAULT
#include "protolink.h"
void main(void)
{
protolink_io_init();
protolink_timer0_init();
protolink_interrupts_init();
protolink_smt_init();
while(TRUE)
{
if (protolink_one_second())
{
protolink_debug_data("INT0: %lu pps, INT1: %lu pps\r\n", flow1_pps, flow2_pps);
protolink_debug_data("SMT1: %lu pps, SMT2: %lu pps\r\n", flow1_smt_pps, flow2_smt_pps);
}
// Aquí va la lógica de tu aplicación (Modbus, MQTT, etc.)
}
}Al activar PROTOLINK_DEFAULT, este header toma control de:
Timer0 (base de tiempo ~1 s).
INT0 / INT1 (entradas de pulsos en IN_0 / IN_1).
SMT1 / SMT2 (conteo de pulsos).
UART5 como canal de debug (DEBUG).
Si tu aplicación requiere alguno de estos periféricos para otro propósito, deberás:
Ajustar la configuración en este header, o
Deshabilitar PROTOLINK_DEFAULT y gestionar tu propia inicialización.