TODOPIC

Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado por: misterweb en 06 de Septiembre de 2025, 15:10:31

Título: Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 15:10:31
 Muy buenas, hace muchísimo tiempo intenté programar con SDCC PIC y la verdad es que me resultó muy frustrante porque había muy poca información y la verdad es que tampoco este mini lenguaje de programación no da mucha cobertura a los microcontroladores PIC.

Pero bueno, hace poco me puse a volver a intentar programar de nuevo, y buscando información por ahí, y probando multitud de veces y también con ayuda de la inteligencia artificial, he conseguido hacerme con una colección de programas que resultan interesantes. 

La verdad es que a estas alturas intentar programar PIC con SDCC cuando existen placas de microcontroladores ESP32, RSP2040, STM32, etc... puede que no resulte muy interesante.

Siempre me resultó agradable compilar en Linux un programa mediante una línea de comando, que ocupa solo unos megas, y luego programar el PIC con el PICkit2 también a través de una línea de comando, sin tener que recurrir a programas muy grandes.

En cualquier caso, ahí lo dejo por si acaso todavía hay todavia alguien que le pueda interesar esto... :) :)

Comentar que compilo los programas en Linux y programo el Pic con el Pickit2 mediante el comando pk2cmd, no se que tal iran este compilador bajo Windows o si existe como tal el comando pk2cmd para windows.

Comentar tambien algunos veces tambien en linux he observado que algunos paquetes de instalacion de SDCC no funcionan bien y no encuentran las ruta.

He creado un paquete de instalacion para Debian de los programas necesarios que serian  SDCC-4.5.0,  gnutils  y pk2cmd, que los subire proximamente, aunque yo habitualmente utilizo Puppy Linux.

Estos programas estan echos para el PIC18F4550 pero se podrian trasladar a un monton de PIC con los que se puede compilar en el SDCC.

Para compilarlo:

sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=40000000 EJERC_01.c

Para programar el Pic como comentaba yo lo hago atraves del PIckit2 supongo que tambien se podra hacer con el PIckit3 pero no lo he probado.

Hay que tener instalado el programa pk2cmd y las gputils.

Tener conectado el Pickit al  microcontrolador y ejecutar:

pk2cmd -P PIC18F4550 -X -M -F EJERC_01.hex


Aqui dejo el primer programa de Blink.

Código: [Seleccionar]
// Compilar con:
//   sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=40000000 EJERC_01.c

// EJERC_01.c PARA VER EL BLINK O APAGAR Y ENCENDER UN LED CADA MEDIO SEGUNDO CONECTADO A
// LA PATITA RC0 DEL MICROCONTROLADOR.


#include <pic18f4550.h>

// Configuración del oscilador para 8 MHz con PLL a 48 MHz

#pragma config FOSC = HSPLL_HS // Oscilador HS con PLL habilitado
#pragma config PLLDIV = 2   // 8 MHz / 2 = 4 MHz → PLL x24 = 96 MHz
#pragma config CPUDIV = OSC1_PLL2 // Usa PLL como fuente (CPU = 40 MHz)
#pragma config USBDIV = 2      // Divide PLL para USB (48 MHz)

// Otras configuraciones importantes
#pragma config PWRT = OFF         // Power-up Timer OFF
#pragma config BOR = OFF          // Brown-out Reset OFF
#pragma config BORV = 3           // BOR Voltage Level (3=2.0V)
#pragma config VREGEN = OFF       // Voltage Regulator OFF
#pragma config STVREN = ON        // Stack Overflow Reset ON
#pragma config LVP = OFF       // Low-Voltage Programming deshabilitado
#pragma config ICPRT = OFF        // ICSP Port OFF
#pragma config XINST = OFF        // Extended Instruction Set OFF
#pragma config WDT = OFF       // Watchdog Timer deshabilitado


// Delay aproximado para milisegundos (ajustado para SDCC)
void delay_ms(int ms) {
    int i, j;  
for (i = 0; i < ms; i++) {
        for (j = 0; j < 1500; j++) {  // 1428 iteraciones ≈ 1 ms
            __asm__("nop");  // 1 ciclo (0.1 µs)
        }
     }
}


void main() {


    PORTA = 0x00;
    LATA =  0x00; 
    CMCON = 0x07;
   
    ADCON1=0x0F;   
   
    TRISA = 0x00;  // Todos los pines de PORTA como salidas


   
    // ¡El PLL se habilita automáticamente con FOSC = HSPLL_HS!
    // No es necesario tocar OSCTUNE manualmente.

    while (1) {
        PORTAbits.RA5 = 0;  // LED OFF
        delay_ms(500);      // 1s
        PORTAbits.RA5 = 1;  // LED ON
        delay_ms(500);      // 1s
    }
}


  - Tienes que ingresar para ver archivos adjuntos -  

_______________________________________________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 15:16:22
Aqui dejo un segundo programa que es el blink pero con la interrupcion de Timer 1.

Para compilarlo y programarlo igual que el primero.

// Compilar con:
// sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=40000000 EJERC_03.c

Código: [Seleccionar]
#include <pic18f4550.h>

// Configuración del oscilador para 8 MHz con PLL a 48 MHz

#pragma config FOSC = HSPLL_HS // Oscilador HS con PLL habilitado
#pragma config PLLDIV = 2   // 8 MHz / 2 = 4 MHz → PLL x24 = 96 MHz
#pragma config CPUDIV = OSC1_PLL2 // Usa PLL como fuente (CPU = 40 MHz)
#pragma config USBDIV = 2      // Divide PLL para USB (48 MHz)

// Otras configuraciones importantes
#pragma config PWRT = OFF         // Power-up Timer OFF
#pragma config BOR = OFF          // Brown-out Reset OFF
#pragma config BORV = 3           // BOR Voltage Level (3=2.0V)
#pragma config VREGEN = OFF       // Voltage Regulator OFF
#pragma config STVREN = ON        // Stack Overflow Reset ON
#pragma config LVP = OFF       // Low-Voltage Programming deshabilitado
#pragma config ICPRT = OFF        // ICSP Port OFF
#pragma config XINST = OFF        // Extended Instruction Set OFF
#pragma config WDT = OFF       // Watchdog Timer deshabilitado


#define LED_LAT     LATAbits.LATA5
#define LED_TRIS    TRISAbits.TRISA5


volatile unsigned char toggle_flag = 0;
volatile unsigned char toggle_counter = 0;  // Contador de interrupciones


void timer1_isr(void) __interrupt(1) {
    if (PIR1bits.TMR1IF) {
        PIR1bits.TMR1IF = 0;        // Limpiar bandera
        TMR1H = 0x0B;               // Recarga para 500 ms @ 48 MHz, preescaler 1:8
        TMR1L = 0xDC;
       
        toggle_counter++;            // Incrementar contador
        if (toggle_counter >= 12) {  // Esperar 2 interrupciones (500ms x 2 = 1 segundo)
            toggle_counter = 0;
            toggle_flag = 1;         // Activar bandera cada 1 segundo
        }
    }
}

void main() {
    TRISAbits.TRISA5 = 0;           // RA5 como salida
    LED_LAT = 0;                    // LED inicialmente apagado

    // Configuración Timer1 (500 ms)
    T1CON = 0x3D;                   // Timer1 ON, preescaler 1:8, oscilador habilitado, asíncrono
    TMR1H = 0x0B;                   // Valores iniciales para 500 ms
    TMR1L = 0xDC;

    // Habilitar interrupciones
    INTCONbits.GIE = 1;             // Interrupciones globales
    INTCONbits.PEIE = 1;            // Interrupciones periféricas
    PIE1bits.TMR1IE = 1;            // Interrupción del Timer1
    PIR1bits.TMR1IF = 0;            // Limpiar bandera

    while(1) {
        if (toggle_flag) {
            toggle_flag = 0;
            LED_LAT = !LED_LAT;      // Cambiar estado del LED cada 1 segundo
        }
    }
}


_______________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)



Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 15:32:57
El siguiente programa es para ver la salida de datos por el puerto serie, consta de dos programas que son:
EJERC_04   que es el principal.
SERIAL.C  para manejo del puerto serie.

Para compilarlo y grabar el pic yo suelo hacer un script en  Linux como el que muestro a continuacion:

Código: [Seleccionar]
#!/bin/sh
# EJERC_04 Compilar con:
# Programa para enviar por el puerto seri el mensaje

# Compilar con:
# ESTE PROGRAMA EJERC_04.c NECESITA DE LA SURUTINA SERIAL.c Y ARCHIVO DE INCLUSION SERIAL.h.
# PARA COMPILAR ESTE PROGRAMA TENEMOS QUE HACER PRIMERO LA COMPILACION DE EJERC_04.c
# Y LUEGO LA COMPILACION DE SERIAL.c COMO SE MUESTRAN EN LAS DOS INSTRUCCIONES.

sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 EJERC_04.c
sleep .5
sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 SERIAL.c
sleep .5

# LUEGO SE COMPILADA LOS DOS ARCHIVOS OBJETO AL EJECUTABLE  EJERC_04.HEX
sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=48000000 EJERC_04.o SERIAL.o

# con Picsimlab se tiene que simulara a 10 Mhz pero esto puede ser un problema de Picsimlab
# hay que ver con el PIc haber que tal.
sleep 1
pk2cmd -P PIC18F4550 -X -M -F EJERC_04.hex


El programa EJERC_04 :

Código: [Seleccionar]
#include <pic18f4550.h>
#include <string.h>
#include <stdint.h>
#include "SERIAL.h"

// Configuración del oscilador para 8 MHz con PLL a 48 MHz

#pragma config FOSC = HSPLL_HS // Oscilador HS con PLL habilitado
#pragma config PLLDIV = 2   // 8 MHz / 2 = 4 MHz → PLL x24 = 96 MHz
#pragma config CPUDIV = OSC1_PLL2 // Usa PLL como fuente (CPU = 40 MHz)
#pragma config USBDIV = 2      // Divide PLL para USB (48 MHz)

// Otras configuraciones importantes
#pragma config PWRT = OFF         // Power-up Timer OFF
#pragma config BOR = OFF          // Brown-out Reset OFF
#pragma config BORV = 3           // BOR Voltage Level (3=2.0V)
#pragma config VREGEN = OFF       // Voltage Regulator OFF
#pragma config STVREN = ON        // Stack Overflow Reset ON
#pragma config LVP = OFF       // Low-Voltage Programming deshabilitado
#pragma config ICPRT = OFF        // ICSP Port OFF
#pragma config XINST = OFF        // Extended Instruction Set OFF
#pragma config WDT = OFF       // Watchdog Timer deshabilitado



void my_strcpy(char *dest, const char *src);
void floattostr(float numero_, unsigned char *cadena_,char decimales_);


void main(void) {

    init_uart();
    TRISCbits.RC0 = 0; // LED como salida
    char caracter = 'A';

    // Variables de diferentes tipos
char letra = 'A';
char mensaje[] = "Hola PIC";
char buffer[0];
int contador = 100;
uint16_t adc_value = 0x0FFF;
float voltaje = 3.1416;

    while(1) {
//Serial_Write("Caracter: %c\n", 'A');
// Serial_Write("Cadena: %s\n", "Hola mundo");
// Serial_Write("Entero: %d\n", -123);
// Serial_Write("Entero 8 bits: %u8\n", 255);



// Uso con variables
Serial_Write("Caracter: %c\n", letra);
Serial_Write("Mensaje: %s\n", mensaje);
Serial_Write("Contador: %d\n", contador);
Serial_Write("Contador: %o8\n", contador);
Serial_Write("Contador: %o16\n", contador);
Serial_Write("ADC: %h8 \n", contador);
Serial_Write("ADC: %h16 \n", adc_value);


int precision=2;
ftoa(voltaje,  buffer,  precision);

Serial_Write("Test: %f \n", buffer);   // Opción 2

Serial_Write("\n Cadena: %s\n", "======================");


        // Esperar 2 segundos con LED intermitente
        for(uint8_t i=0; i<4; i++) {
            PORTCbits.RC0 = 0;
            delay_ms(1000);
            PORTCbits.RC0 = 1;
            delay_ms(1000);
        }
    }
}


// Nuestra función para copiar cadenas
void my_strcpy(char *dest, const char *src) {
    while (*src != '\0') {
        *dest++ = *src++;
    }
    *dest = '\0'; // Asegurar terminación nula
}


char* ftoa(float num, char* buffer, int precision) {
    int i = 0;
    int sign = 1;
   
    // Manejar números negativos
    if (num < 0) {
        sign = -1;
        num = -num;
    }
   
    // Parte entera
    int intPart = (int)num;
    // Parte decimal
    float decimalPart = num - intPart;
   
    // Convertir parte entera
    char intBuffer[20];
    int intIdx = 0;
   
    if (intPart == 0) {
        intBuffer[intIdx++] = '0';
    } else {
        while (intPart > 0) {
            intBuffer[intIdx++] = (intPart % 10) + '0';
            intPart /= 10;
        }
    }
   
    // Agregar signo si es negativo
    if (sign == -1) {
        buffer[i++] = '-';
    }
   
    // Invertir la parte entera y copiar al buffer
    for (int j = intIdx - 1; j >= 0; j--) {
        buffer[i++] = intBuffer[j];
    }
   
    // Agregar punto decimal si hay precisión
    if (precision > 0) {
        buffer[i++] = '.';
       
        // Convertir parte decimal
        for (int p = 0; p < precision; p++) {
            decimalPart *= 10;
            int digit = (int)decimalPart;
            buffer[i++] = digit + '0';
            decimalPart -= digit;
        }
    }
   
    buffer[i] = '\0'; // Terminar la cadena
   
    return buffer;
}

void floattostr(float numero_, unsigned char *cadena_,char decimales_)
{
    int largo_entera,largo_n,cont_for,tempo_int;
    uint16_t tempo_float;
    largo_n = decimales_+1;
    largo_entera = 0;

    if(numero_ < 0)
    {
        *cadena_++ = '-';
        numero_ = -numero_;
    }
    if(numero_ > 0.0) while (numero_ < 1.0)
    {
        numero_ =numero_* 10.0;
        largo_entera--;
    }
    while(numero_ >= 10.0)
    {
        numero_ = numero_/10.0;
        largo_entera++;
    }
    largo_n = largo_n+largo_entera;
   
    for(tempo_float = cont_for = 1; cont_for < largo_n; cont_for++)
        tempo_float = tempo_float/10.0;
    numero_ += tempo_float/2.0;
    if(numero_ >= 10.0)
    {
        numero_ = 1.0; largo_entera++;
    }
    if(largo_entera<0)
    {
        *cadena_++ = '0'; *cadena_++ = '.';
        if(largo_n < 0) largo_entera = largo_entera-largo_n;
        for(cont_for = -1; cont_for > largo_entera; cont_for--) *cadena_++ = '0';
    }
    for(cont_for=0; cont_for < largo_n; cont_for++)
    {
        tempo_int = numero_;
        *cadena_++ = tempo_int + 48;
        if (cont_for ==  largo_entera ) *cadena_++ = '.';
        numero_ -= (tempo_float=tempo_int);
        numero_ = numero_*10.0;
    }
    *cadena_ = 0;
}



 

Y el programa  SERIAL.c :

Código: [Seleccionar]
    #include "SERIAL.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>


// Configurar UART a 9600 baudios (para 40 MHz)
void init_uart(void) {
    TRISCbits.TRISC6 = 0;   // TX (RC6) como salida
    TRISCbits.TRISC7 = 1;   // RX (RC7) como entrada
   
    SPBRG = 155;             // 9600 baudios @ 40 MHz
    RCSTA = 0x90;     
    TXSTAbits.BRGH = 1;     // Alta velocidad
    TXSTAbits.SYNC = 0;     // Modo asíncrono
    RCSTAbits.SPEN = 1;     // Habilitar puerto serial
    TXSTAbits.TXEN = 1;     // Habilitar transmisión
}

// Enviar cadena por UART
void send_serial(const char *str) {
    while (*str) {
        while (!TXSTAbits.TRMT);
        TXREG = *str++;
    }
}

// Función auxiliar para comparar los próximos caracteres del formato
static uint8_t format_matches(const char **format, const char *match) {
    const char *f = *format;
    const char *m = match;
   
    while (*m != '\0') {
        if (*f != *m) {
            return 0; // No coincide
        }
        f++;
        m++;
    }
   
    // Coincide, avanzamos el puntero del formato
    *format = f;
    return 1;
}

void Serial_Write(const char *format, ...) {
    va_list args;
    va_start(args, format);
   
    while (*format != '\0') {
        if (*format == '%') {
            format++; // Avanzar al caracter después del %
           
            // Manejar los diferentes especificadores de formato
            if (*format == 'c') {
                char c = (char)va_arg(args, int);
                send_char(c);
            }
            else if (*format == 's') {
                char *str = va_arg(args, char*);
                send_string(str);
            }
            else if (*format == 'd') {
                int num = va_arg(args, int);
                send_int(num);
            }
            else if (*format == 'u') {
                format++; // Avanzar al número
                if (*format == '8') {
                    uint8_t num = (uint8_t)va_arg(args, int);
                    send_uint8(num);
                }
                else if (*format == '1' && *(format+1) == '6') {
                    format++; // Saltar el '1'
                    uint16_t num = (uint16_t)va_arg(args, int);
                    send_uint16(num);
                }
                else if (*format == '3' && *(format+1) == '2') {
                    format++; // Saltar el '3'
                    uint32_t num = va_arg(args, uint32_t);
                    send_uint32(num);
                }
                else {
                    // Formato desconocido, enviar literal
                    send_char('%');
                    send_char('u');
                    format--; // Retroceder para mostrar el caracter desconocido
                }
            }

else if (*format == 'f') {
char *buffer = va_arg(args, char*);
send_string(buffer);

}


            else if (*format == 'h') {
                format++; // Avanzar al número
                if (*format == '8') {
                    uint8_t num = (uint8_t)va_arg(args, int);
                    send_serial("0x");
                    send_hex8(num);
                }
                else if (*format == '1' && *(format+1) == '6') {
                    format++; // Saltar el '1'
                    uint16_t num = (uint16_t)va_arg(args, int);
                    send_serial("0x");
                    send_hex16(num);
                }
                else {
                    // Formato desconocido, enviar literal
                    send_char('%');
                    send_char('h');
                    format--; // Retroceder para mostrar el caracter desconocido
                }
            }
            else if (*format == 'o') {
                format++; // Avanzar al número
                if (*format == '8') {
                    uint8_t num = (uint8_t)va_arg(args, int);
                    send_octal8(num);
                }
                else if (*format == '1' && *(format+1) == '6') {
                    format++; // Saltar el '1'
                    uint16_t num = (uint16_t)va_arg(args, int);
                    send_octal16(num);
                }
                else {
                    // Formato desconocido, enviar literal
                    send_char('%');
                    send_char('o');
                    format--; // Retroceder para mostrar el caracter desconocido
                }
            }
            else {
                // Caracter desconocido después de %, lo enviamos tal cual
                send_char('%');
                send_char(*format);
            }
            format++; // Avanzar al siguiente caracter
        } else {
            send_char(*format);
            format++;
        }
    }
   
    va_end(args);
}



// Enviar un solo carácter
void send_char(char c) {
    while (!TXSTAbits.TRMT);
    TXREG = c;
}

// Función auxiliar para enviar dígitos
void send_digits(uint16_t num) {
    if (num >= 10) {
        send_digits(num / 10);
    }
    send_char('0' + (num % 10));
}

// Enviar número entero de 8 bits sin signo
void send_uint8(uint8_t num) {
    if (num == 0) {
        send_char('0');
        return;
    }
    send_digits(num);
}

// Enviar número entero de 16 bits sin signo
void send_uint16(uint16_t num) {
    if (num == 0) {
        send_char('0');
        return;
    }
    send_digits(num);
}

// Enviar número entero de 32 bits sin signo
void send_uint32(uint32_t num) {
    if (num == 0) {
        send_char('0');
        return;
    }
    send_digits32(num);
}

// Función auxiliar para enviar dígitos de 32 bits
void send_digits32(uint32_t num) {
    if (num >= 10) {
        send_digits32(num / 10);
    }
    send_char('0' + (num % 10));
}


// Enviar número entero con signo
void send_int(int num) {
    if (num < 0) {
        send_char('-');
        num = -num;
    }
    send_uint16((uint16_t)num);
}

void send_float(float num, uint8_t decimales) {
    // Parte entera
    int32_t entero = (int32_t)num;
    if(num < 0) {
        send_char('-');
        entero = -entero;
        num = -num;
    }
    send_digits32(entero);
   
    // Parte decimal
    send_char('.');
    float fraccion = num - (float)entero;
    for(uint8_t i = 0; i < decimales; i++) {
        fraccion *= 10.0f;
        uint8_t digito = (uint8_t)fraccion;
        send_char('0' + digito);
        fraccion -= digito;
    }
}


// Recibir carácter por UART (retorna 0 si no hay dato)
char receive_serial(void) {
    if (PIR1bits.RCIF) {
        if (RCSTAbits.OERR) {
            RCSTAbits.CREN = 0;
            RCSTAbits.CREN = 1;
        }
        return RCREG;
    }
    return 0;
}

// Retardos básicos
void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 1000; j++);
    }
}

void delay_us(unsigned int us) {
    unsigned int i;
    for(i = 0; i < us; i++);
}

// Enviar número en hexadecimal (8 bits)
void send_hex8(uint8_t num) {
    char nibble;
    //send_serial("0x");
   
    // Primer nibble (4 bits superiores)
    nibble = (num >> 4) & 0x0F;
    send_char(nibble > 9 ? ('A' + nibble - 10) : ('0' + nibble));
   
    // Segundo nibble (4 bits inferiores)
    nibble = num & 0x0F;
    send_char(nibble > 9 ? ('A' + nibble - 10) : ('0' + nibble));
}

// Enviar número en hexadecimal (16 bits)
void send_hex16(uint16_t num) {
    //send_serial("0x");
    send_hex8((uint8_t)(num >> 8));  // Byte alto
    send_hex8((uint8_t)num);         // Byte bajo
}

// Enviar número en octal (8 bits)
void send_octal8(uint8_t num) {
    send_char('0'); // Prefijo octal
    if (num < 8) {
        send_char('0' + num);
        return;
    }
   
    char buffer[4]; // Máximo 3 dígitos octal para 8 bits
    uint8_t i = 0;
   
    while (num > 0) {
        buffer[i++] = '0' + (num % 8);
        num /= 8;
    }
   
    // Imprimir en orden inverso
    while (i > 0) {
        send_char(buffer[--i]);
    }
}

// Enviar número en octal (16 bits)
void send_octal16(uint16_t num) {
    send_char('0'); // Prefijo octal
    if (num < 8) {
        send_char('0' + num);
        return;
    }
   
    char buffer[6]; // Máximo 6 dígitos octal para 16 bits
    uint8_t i = 0;
   
    while (num > 0) {
        buffer[i++] = '0' + (num % 8);
        num /= 8;
    }
   
    // Imprimir en orden inverso
    while (i > 0) {
        send_char(buffer[--i]);
    }
}

// Enviar número en binario (8 bits)
void send_binary8(uint8_t num) {
    send_serial("0b");
    for (int8_t i = 7; i >= 0; i--) {
        send_char((num & (1 << i)) ? '1' : '0');
    }
}

// Enviar número en binario (16 bits)
void send_binary16(uint16_t num) {
    send_serial("0b");
    for (int8_t i = 15; i >= 0; i--) {
        send_char((num & (1 << i)) ? '1' : '0');
    }
}

// Función para enviar una variable de cadena (array de caracteres)
void send_string(char *str) {
    while (*str != '\0') {  // Hasta encontrar el carácter nulo
        while (!TXSTAbits.TRMT); // Esperar buffer libre
        TXREG = *str++;
    }
}

uint8_t serial_available(void) {
    // Para PIC18F4550 con UART:
    return PIR1bits.RCIF;  // Retorna 1 si hay datos en el buffer de recepción
   
    // Alternativa genérica si RCIF no está disponible:
    // return RCREG;  // Lee el registro para ver si hay datos
}

 


  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  


  - Tienes que ingresar para ver archivos adjuntos -  

_____________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)




Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 15:46:42
 El siguiente programa es referente al puerto serie , pero en este caso se le mete un comando o numeros  1 , 2 y 3 y responde con unos mensajes.


Consta de los mismos archivos como el anterior:

Código: [Seleccionar]
// EJERC_05 Compilar con:
// Programa que recibe por el puerto serie los caracteres 1 , 2 y 3 que nosotros enviaremos
// Y nos contestara con: Recibido valor X  siendo X el caracter recibido.
// Compilar con:
// ESTE PROGRAMA EJERC_05.c NECESITA DE LA SURUTINA SERIAL.c Y ARCHIVO DE INCLUSION SERIAL.h.
// PARA COMPILAR ESTE PROGRAMA TENEMOS QUE HACER PRIMERO LA COMPILACION DE EJERC_05.c
// Y LUEGO LA COMPILACION DE SERIAL.c COMO SE MUESTRAN EN LAS DOS INSTRUCCIONES.

//sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 EJERC_05.c
//sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 SERIAL.c

// LUEGO SE COMPILADA LOS DOS ARCHIVOS OBJETO AL EJECUTABLE  EJERC_05.HEX
// sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=48000000 EJERC_05.o SERIAL.o


#include <pic18f4550.h>
#include <string.h>
#include "SERIAL.h"

// Configuración del oscilador para 8 MHz con PLL a 48 MHz

#pragma config FOSC = HSPLL_HS // Oscilador HS con PLL habilitado
#pragma config PLLDIV = 2   // 8 MHz / 2 = 4 MHz → PLL x24 = 96 MHz
#pragma config CPUDIV = OSC1_PLL2 // Usa PLL como fuente (CPU = 40 MHz)
#pragma config USBDIV = 2      // Divide PLL para USB (48 MHz)

// Otras configuraciones importantes
#pragma config PWRT = OFF         // Power-up Timer OFF
#pragma config BOR = OFF          // Brown-out Reset OFF
#pragma config BORV = 3           // BOR Voltage Level (3=2.0V)
#pragma config VREGEN = OFF       // Voltage Regulator OFF
#pragma config STVREN = ON        // Stack Overflow Reset ON
#pragma config LVP = OFF       // Low-Voltage Programming deshabilitado
#pragma config ICPRT = OFF        // ICSP Port OFF
#pragma config XINST = OFF        // Extended Instruction Set OFF
#pragma config WDT = OFF       // Watchdog Timer deshabilitado



// Mensajes de respuesta
const char msg1[] = "Recibido valor 1\n";
const char msg2[] = "Recibido valor 2\n";
const char msg3[] = "Recibido valor 3\n";
const char msg_unknown[] = "Valor no reconocido\n";
const char wellcome[] = "Bienvenido recepcion comandos \n";

void main() {
    init_uart();    // Configurar UART
    TRISCbits.RC0 = 0; // Configurar LED como salida
    send_serial(wellcome);
   
   
    while (1) {
        char received = receive_serial();
       
        if (received != 0) { // Si se recibió algo
            PORTCbits.RC0 = 1; // Encender LED al recibir
           
            switch(received) {
                case '1':
                    send_serial(msg1);
                    break;
                case '2':
                    send_serial(msg2);
                    break;
                case '3':
                    send_serial(msg3);
                    break;
                default:
                    send_serial(msg_unknown);
            }
           
            delay_ms(100); // Pequeño retardo para estabilidad
            PORTCbits.RC0 = 0; // Apagar LED
        }
       
        delay_ms(10); // Pequeño retardo entre chequeos
    }
}



Y SERIAL.c

Código: [Seleccionar]
#include "SERIAL.h"

// Configurar UART a 9600 baudios (para 40 MHz)
void init_uart(void) {
    TRISCbits.TRISC6 = 0;   // TX (RC6) como salida
    TRISCbits.TRISC7 = 1;   // RX (RC7) como entrada
   
    SPBRG = 155;             // 9600 baudios @ 40 MHz
    RCSTA = 0x90;     
    TXSTAbits.BRGH = 1;     // Alta velocidad
    TXSTAbits.SYNC = 0;     // Modo asíncrono
    RCSTAbits.SPEN = 1;     // Habilitar puerto serial
    TXSTAbits.TXEN = 1;     // Habilitar transmisión
}

// Enviar cadena por UART
void send_serial(const char *str) {
    while (*str) {
        while (!TXSTAbits.TRMT); // Esperar a que el buffer esté libre
        TXREG = *str++;          // Enviar carácter
    }
}

// Recibir carácter por UART (retorna 0 si no hay dato)
char receive_serial(void) {
    if (PIR1bits.RCIF) {        // Si hay dato recibido
        if (RCSTAbits.OERR) {    // Si hay overrun error
            RCSTAbits.CREN = 0;  // Limpiar error
            RCSTAbits.CREN = 1;  // Rehabilitar recepción
        }
        return RCREG;           // Retornar carácter recibido
    }
    return 0;                   // Retornar 0 si no hay dato
}

// Retardos básicos
void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 1000; j++);
    }
}

void delay_us(unsigned int us) {
    unsigned int i;
    for(i = 0; i < us; i++);
}



Podemos hacer un script para compilar y programar el PIC.

Código: [Seleccionar]
#!/bin/sh
# EJERC_05 Compilar con:
# Programa para enviar por el puerto seri el mensaje "¿Como estan ustedes?"

# Compilar con:
# ESTE PROGRAMA EJERC_04.c NECESITA DE LA SURUTINA SERIAL.c Y ARCHIVO DE INCLUSION SERIAL.h.
# PARA COMPILAR ESTE PROGRAMA TENEMOS QUE HACER PRIMERO LA COMPILACION DE EJERC_05.c
# Y LUEGO LA COMPILACION DE SERIAL.c COMO SE MUESTRAN EN LAS DOS INSTRUCCIONES.

sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 EJERC_05.c
sleep .5
sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 SERIAL.c
sleep .5

# LUEGO SE COMPILADA LOS DOS ARCHIVOS OBJETO AL EJECUTABLE  EJERC_05.HEX
sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=48000000 EJERC_05.o SERIAL.o

sleep 1
pk2cmd -P PIC18F4550 -X -M -F EJERC_05.hex


  - Tienes que ingresar para ver archivos adjuntos -

  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  

 

 



______________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)

Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 15:51:24
El siguiente programa para manejar la pantalla del nokia5110.

Código: [Seleccionar]
// EJERC_06.c
// Programa para mostrar el funcionamiento del panel NOKIA5110 "
// Compilar con:
// ESTE PROGRAMA EJERC_06.c NECESITA DE LA SURUTINA NOKIA5110.c Y EL ARCHIVO NOKIA5110.h.
// PARA COMPILAR ESTE PROGRAMA TENEMOS QUE HACER PRIMERO LA COMPILACION DE EJERC_06.c
// Y LUEGO LA COMPILACION DE NOKIA5110.c COMO SE MUESTRAN EN LAS DOS INSTRUCCIONES.

//sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 EJERC_06.c
//sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 NOKIA5110.c

//LUEGO SE COMPILADA LOS DOS ARCHIVOS OBJETO AL EJECUTABLE  EJERC_06.HEX
//sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=48000000 EJERC_06.o NOKIA5110.o

#include <pic18f4550.h>
#include <string.h>
#include "NOKIA5110.h"


// Configuración del oscilador para 8 MHz con PLL a 48 MHz

#pragma config FOSC = HSPLL_HS // Oscilador HS con PLL habilitado
#pragma config PLLDIV = 2   // 8 MHz / 2 = 4 MHz → PLL x24 = 96 MHz
#pragma config CPUDIV = OSC1_PLL2 // Usa PLL como fuente (CPU = 40 MHz)
#pragma config USBDIV = 2      // Divide PLL para USB (48 MHz)

// Otras configuraciones importantes
#pragma config PWRT = OFF         // Power-up Timer OFF
#pragma config BOR = OFF          // Brown-out Reset OFF
#pragma config BORV = 3           // BOR Voltage Level (3=2.0V)
#pragma config VREGEN = OFF       // Voltage Regulator OFF
#pragma config STVREN = ON        // Stack Overflow Reset ON
#pragma config LVP = OFF       // Low-Voltage Programming deshabilitado
#pragma config ICPRT = OFF        // ICSP Port OFF
#pragma config XINST = OFF        // Extended Instruction Set OFF
#pragma config WDT = OFF       // Watchdog Timer deshabilitado



void main() {
    // Inicializar hardware
    SPI_Init();
    LCD_Init();
    LCD_Clear();
   
    // Mostrar mensaje de prueba
    LCD_Text("  PIC18F4550", 0, 0);
    LCD_Text("  con Nokia5110", 0, 1);
    LCD_Text("  usando SDCC", 0, 2);
    LCD_Text("  ¡Funciona!", 0, 3);
   
    while (1) {
        // Puedes añadir aquí código adicional
        // como animaciones o actualizaciones
    }
}


Y ahora el archivo  NOKIA54110.c

Código: [Seleccionar]
#include "NOKIA5110.h"
// Fuente básica 5x7 (solo caracteres ASCII 32-126)
const unsigned char font[][5] = {
    {0x00, 0x00, 0x00, 0x00, 0x00}, // Espacio (ASCII 32)
    {0x00, 0x00, 0x5F, 0x00, 0x00}, // !
    {0x00, 0x07, 0x00, 0x07, 0x00}, // "
    {0x14, 0x7F, 0x14, 0x7F, 0x14}, // #
    {0x24, 0x2A, 0x7F, 0x2A, 0x24}, // $
    {0x23, 0x13, 0x08, 0x64, 0x62}, // %
    {0x36, 0x49, 0x55, 0x22, 0x50}, // &
    {0x00, 0x05, 0x03, 0x00, 0x00}, // '
    {0x00, 0x1C, 0x22, 0x41, 0x00}, // (
    {0x00, 0x41, 0x22, 0x1C, 0x00}, // )
    {0x08, 0x2A, 0x1C, 0x2A, 0x08}, // *
    {0x08, 0x08, 0x3E, 0x08, 0x08}, // +
    {0x00, 0x50, 0x30, 0x00, 0x00}, // ,
    {0x08, 0x08, 0x08, 0x08, 0x08}, // -
    {0x00, 0x60, 0x60, 0x00, 0x00}, // .
    {0x20, 0x10, 0x08, 0x04, 0x02}, // /
    {0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0
    {0x00, 0x42, 0x7F, 0x40, 0x00}, // 1
    {0x42, 0x61, 0x51, 0x49, 0x46}, // 2
    {0x21, 0x41, 0x45, 0x4B, 0x31}, // 3
    {0x18, 0x14, 0x12, 0x7F, 0x10}, // 4
    {0x27, 0x45, 0x45, 0x45, 0x39}, // 5
    {0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6
    {0x01, 0x71, 0x09, 0x05, 0x03}, // 7
    {0x36, 0x49, 0x49, 0x49, 0x36}, // 8
    {0x06, 0x49, 0x49, 0x29, 0x1E}, // 9
    {0x00, 0x36, 0x36, 0x00, 0x00}, // :
    {0x00, 0x56, 0x36, 0x00, 0x00}, // ;
    {0x08, 0x14, 0x22, 0x41, 0x00}, // <
    {0x14, 0x14, 0x14, 0x14, 0x14}, // =
    {0x00, 0x41, 0x22, 0x14, 0x08}, // >
    {0x02, 0x01, 0x51, 0x09, 0x06}, // ?
    {0x3E, 0x41, 0x5D, 0x55, 0x1E}, // @
    {0x7E, 0x09, 0x09, 0x09, 0x7E}, // A
    {0x7F, 0x49, 0x49, 0x49, 0x36}, // B
    {0x3E, 0x41, 0x41, 0x41, 0x22}, // C
    {0x7F, 0x41, 0x41, 0x22, 0x1C}, // D
    {0x7F, 0x49, 0x49, 0x49, 0x41}, // E
    {0x7F, 0x09, 0x09, 0x09, 0x01}, // F
    {0x3E, 0x41, 0x49, 0x49, 0x7A}, // G
    {0x7F, 0x08, 0x08, 0x08, 0x7F}, // H
    {0x00, 0x41, 0x7F, 0x41, 0x00}, // I
    {0x20, 0x40, 0x41, 0x3F, 0x01}, // J
    {0x7F, 0x08, 0x14, 0x22, 0x41}, // K
    {0x7F, 0x40, 0x40, 0x40, 0x40}, // L
    {0x7F, 0x02, 0x04, 0x02, 0x7F}, // M
    {0x7F, 0x04, 0x08, 0x10, 0x7F}, // N
    {0x3E, 0x41, 0x41, 0x41, 0x3E}, // O
    {0x7F, 0x09, 0x09, 0x09, 0x06}, // P
    {0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q
    {0x7F, 0x09, 0x19, 0x29, 0x46}, // R
    {0x26, 0x49, 0x49, 0x49, 0x32}, // S
    {0x01, 0x01, 0x7F, 0x01, 0x01}, // T
    {0x3F, 0x40, 0x40, 0x40, 0x3F}, // U
    {0x1F, 0x20, 0x40, 0x20, 0x1F}, // V
    {0x7F, 0x20, 0x18, 0x20, 0x7F}, // W
    {0x63, 0x14, 0x08, 0x14, 0x63}, // X
    {0x07, 0x08, 0x70, 0x08, 0x07}, // Y
    {0x61, 0x51, 0x49, 0x45, 0x43}, // Z
    {0x00, 0x7F, 0x41, 0x41, 0x00}, // [
    {0x02, 0x04, 0x08, 0x10, 0x20}, // backslash
    {0x00, 0x41, 0x41, 0x7F, 0x00}, // ]
    {0x04, 0x02, 0x01, 0x02, 0x04}, // ^
    {0x40, 0x40, 0x40, 0x40, 0x40}, // _
    {0x00, 0x01, 0x02, 0x04, 0x00}, // `
    {0x20, 0x54, 0x54, 0x54, 0x78}, // a
    {0x7F, 0x44, 0x44, 0x44, 0x38}, // b
    {0x38, 0x44, 0x44, 0x44, 0x28}, // c
    {0x38, 0x44, 0x44, 0x44, 0x7F}, // d
    {0x38, 0x54, 0x54, 0x54, 0x18}, // e
    {0x08, 0x7E, 0x09, 0x09, 0x00}, // f
    {0x18, 0xA4, 0xA4, 0xA4, 0x7C}, // g
    {0x7F, 0x08, 0x04, 0x04, 0x78}, // h
    {0x00, 0x44, 0x7D, 0x40, 0x00}, // i
    {0x00, 0x20, 0x40, 0x40, 0x3D}, // j
    {0x7F, 0x10, 0x28, 0x44, 0x00}, // k
    {0x00, 0x41, 0x7F, 0x40, 0x00}, // l
    {0x7C, 0x04, 0x78, 0x04, 0x78}, // m
    {0x7C, 0x08, 0x04, 0x04, 0x78}, // n
    {0x38, 0x44, 0x44, 0x44, 0x38}, // o
    {0xFC, 0x24, 0x24, 0x24, 0x18}, // p
    {0x18, 0x24, 0x24, 0x24, 0xFC}, // q
    {0x7C, 0x08, 0x04, 0x04, 0x08}, // r
    {0x48, 0x54, 0x54, 0x54, 0x20}, // s
    {0x04, 0x3F, 0x44, 0x44, 0x20}, // t
    {0x3C, 0x40, 0x40, 0x40, 0x3C}, // u
    {0x1C, 0x20, 0x40, 0x20, 0x1C}, // v
    {0x3C, 0x40, 0x30, 0x40, 0x3C}, // w
    {0x44, 0x28, 0x10, 0x28, 0x44}, // x
    {0x1C, 0xA0, 0xA0, 0xA0, 0x7C}, // y
    {0x44, 0x64, 0x54, 0x4C, 0x44}, // z
    {0x00, 0x08, 0x36, 0x41, 0x00}, // {
    {0x00, 0x00, 0x7F, 0x00, 0x00}, // |
    {0x00, 0x41, 0x36, 0x08, 0x00}, // }
    {0x08, 0x04, 0x08, 0x10, 0x08}, // ~
};

// Retardo aproximado para 40 MHz
void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 1200; j++); // Ajustado para 40MHz
    }
}

// Retardo en microsegundos aproximado para 40 MHz
void delay_us(unsigned int us) {
    while(us--) {
        __asm nop __endasm;
        __asm nop __endasm;
        __asm nop __endasm;
        __asm nop __endasm;
    }
}

// Inicializar SPI en modo software (bit-banging)
void SPI_Init() {
    // Configurar pines como salidas
    SCE_DIR = 0;
    RST_DIR = 0;
    DC_DIR = 0;
    DIN_DIR = 0;
    CLK_DIR = 0;
   
    // Estado inicial
    SCE_PIN = 1;  // Chip deshabilitado
    CLK_PIN = 0;  // Reloj bajo
    RST_PIN = 1;  // Reset alto
}

// Enviar byte por SPI (bit-banging)
void SPI_Write(unsigned char data) {
    unsigned char i;
    for (i = 0; i < 8; i++) {
        DIN_PIN = (data & 0x80) ? 1 : 0; // Bit más significativo primero
        CLK_PIN = 1;
        delay_us(5);  // Retardo para asegurar estabilidad
        CLK_PIN = 0;
        delay_us(5);
        data <<= 1;
    }
}

// Enviar comando/dato al LCD
void LCD_Write(unsigned char data, unsigned char mode) {
    SCE_PIN = 0;  // Seleccionar dispositivo
   
    if (mode == LCD_CMD) {
        DC_PIN = 0;  // Modo comando
    } else {
        DC_PIN = 1;  // Modo dato
    }
   
    SPI_Write(data);
    SCE_PIN = 1;  // Deseleccionar dispositivo
    delay_us(10); // Pequeño retardo entre operaciones
}

// Inicializar display
void LCD_Init() {
    // Secuencia de reset
    RST_PIN = 1;
    delay_ms(10);
    RST_PIN = 0;
    delay_ms(10);
    RST_PIN = 1;
    delay_ms(100);  // Retardo después de reset
   
    // Comandos de inicialización
    LCD_Write(0x21, LCD_CMD);  // Modo extendido
    LCD_Write(0xB8, LCD_CMD);  // Voltaje (ajustar si es necesario)
    LCD_Write(0x04, LCD_CMD);  // Temp. coeficiente
    LCD_Write(0x14, LCD_CMD);  // Bias
    LCD_Write(0x20, LCD_CMD);  // Modo básico
    LCD_Write(0x0C, LCD_CMD);  // Display ON
}

// Limpiar pantalla
void LCD_Clear() {
    unsigned int i;
    for (i = 0; i < 504; i++) {  // 84x48 bits = 504 bytes
        LCD_Write(0x00, LCD_DATA);
    }
    // Volver a posición inicial
    LCD_Write(0x80, LCD_CMD);  // X = 0
    LCD_Write(0x40, LCD_CMD);  // Y = 0
}

// Escribir texto en posición (x: 0-83, y: 0-5)
void LCD_Text(const char *str, unsigned char x, unsigned char y) {
    // Establecer posición
    LCD_Write(0x40 | y, LCD_CMD);  // Posición Y (0-5)
    LCD_Write(0x80 | x, LCD_CMD);  // Posición X (0-83)
   
    // Enviar cada carácter
    while (*str) {
        unsigned char c = *str++;
        if (c >= 32 && c <= 126) {  // Solo caracteres válidos
            unsigned char i;
            for (i = 0; i < 5; i++) {
                LCD_Write(font[c - 32][i], LCD_DATA);
            }
            LCD_Write(0x00, LCD_DATA);  // Espacio entre caracteres
        }
    }
}



Para compilarlo podemos hacer el script:

Código: [Seleccionar]
#!/bin/sh
# EJERC_06 Compilar con:

sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 EJERC_06.c
sleep .5
sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 NOKIA5110.c
sleep .5

sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=48000000 EJERC_06.o NOKIA5110.o

sleep 1
pk2cmd -P PIC18F4550 -X -M -F EJERC_06.hex




  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  


____________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)



Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 16:08:20
El siguiente programa para grabar datos en una memoria Eeprom por I2C y luego leerla.

Código: [Seleccionar]
// Compilar con:
// sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=40000000 EJERC_07.c

#include <pic18f4550.h>
#include <stdio.h>
#include <string.h>

#pragma config FOSC = HSPLL_HS, PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config PWRT = OFF, BOR = OFF, BORV = 3, VREGEN = OFF
#pragma config STVREN = ON, LVP = OFF, ICPRT = OFF, XINST = OFF, WDT = OFF


#define EEPROM_ADDR 0xA0  // 24C512 con A0=A1=A2=GND (0b10100000 = 0xA0)
#define MSG_LEN 32

const char msg1[] = "\r\nSistema iniciado. Envie 1, 2 o 3 para probar.\r\n";
const char msg2[] = "Probando conexion I2C con EEPROM...\n";
const char msg3[] = "Recibido valor 3\n";
const char msg_unknown[] = "Valor no reconocido\n";


// Prototipos
void init_uart(void);
void putch(char c);
char getch(void);
void i2c_init(void);
void i2c_start(void);
void i2c_stop(void);
unsigned char i2c_write(unsigned char data);
unsigned char i2c_read(unsigned char ack);
void eeprom_read(unsigned int addr, char *buffer, unsigned char length);
void eeprom_write(unsigned int addr, char *buffer, unsigned char length);
void delay_ms(unsigned int ms);
unsigned char i2c_ping(void);
void init_eeprom_data(void);
unsigned char is_eeprom_empty(unsigned int addr);
void send_serial(const char *str);
char receive_serial(void);


// Direcciones
#define ADDR_MSG1 0x0000
#define ADDR_MSG2 0x0020
#define ADDR_MSG3 0x0040

void main(void) {
    char comando;
    char mensaje[MSG_LEN + 1];
   
    init_uart();
    i2c_init();
   
    delay_ms(500);
    send_serial(msg1);
   
    // Prueba de conexión I2C
    send_serial(msg2);
    if(i2c_ping()) {
        send_serial("OK\r\n");
    } else {
        send_serial("FALLO!\r\n");
        send_serial("Verifique:\r\n");
        send_serial("- Conexiones SCL(RB0) y SDA(RB1)\r\n");
        send_serial("- Resistores pull-up (4.7kΩ) en SCL/SDA\r\n");
        send_serial("- A0,A1,A2 en GND\r\n");
        send_serial("- Alimentacion 5V\r\n");
        while(1);
    }

    // Inicializar datos en EEPROM si está vacía
    init_eeprom_data();

    while(1) {
        comando = getch();
        putch(comando); // Eco
        send_serial("\r\n");
       
        char debug_msg[50];
        strcpy(debug_msg, "\r\nCaracter recibido: 0x");
        char hex[3];
        hex[0] = "0123456789ABCDEF"[(comando >> 4) & 0x0F];
        hex[1] = "0123456789ABCDEF"[comando & 0x0F];
        hex[2] = '\0';
        strcat(debug_msg, hex);
        strcat(debug_msg, "\r\n");
        send_serial(debug_msg);
       
        switch(comando) {
            case '1':
                eeprom_read(ADDR_MSG1, mensaje, MSG_LEN);
                break;
            case '2':
                eeprom_read(ADDR_MSG2, mensaje, MSG_LEN);
                break;
            case '3':
                eeprom_read(ADDR_MSG3, mensaje, MSG_LEN);
                break;
            default:
                strcpy(mensaje, "Comando no valido");
                break;
        }
       
        char output[MSG_LEN + 20];
        strcpy(output, "Mensaje leido: ");
        strcat(output, mensaje);
        strcat(output, "\r\n\r\n");
        send_serial(output);
    }
}

// Función para probar conexión I2C
unsigned char i2c_ping(void) {
    unsigned char ack;
    i2c_start();
    ack = i2c_write(EEPROM_ADDR); // Intenta escribir
    i2c_stop();
    return ack; // 1=exito, 0=fallo
}

// Verifica si la EEPROM está vacía en una dirección específica
unsigned char is_eeprom_empty(unsigned int addr) {
    char buffer[MSG_LEN + 1];
    eeprom_read(addr, buffer, MSG_LEN);
   
    // Verifica si todos los bytes son 0xFF (valor por defecto de EEPROM vacía)
    for(unsigned char i = 0; i < MSG_LEN; i++) {
        if(buffer[i] != 0xFF) {
            return 0; // No está vacía
        }
    }
    return 1; // Está vacía
}

// Inicializa los datos en la EEPROM si es necesario
void init_eeprom_data(void) {
    send_serial("Verificando datos en EEPROM...\r\n");
   
    if(is_eeprom_empty(ADDR_MSG1)) {
        send_serial("Inicializando mensajes en EEPROM...\r\n");
        char msg1[] = "Enviando mensaje 1";
        char msg2[] = "Enviando mensaje 2";
        char msg3[] = "Enviando mensaje 3";
       
        eeprom_write(ADDR_MSG1, msg1, strlen(msg1)+1);
        eeprom_write(ADDR_MSG2, msg2, strlen(msg2)+1);
        eeprom_write(ADDR_MSG3, msg3, strlen(msg3)+1);
       
        send_serial("Mensajes inicializados correctamente.\r\n");
    } else {
        send_serial("EEPROM ya contiene datos, no se inicializa.\r\n");
    }
}

// Escritura en EEPROM
void eeprom_write(unsigned int addr, char *buffer, unsigned char length) {
    unsigned char high_addr = (addr >> 8) & 0xFF;
    unsigned char low_addr = addr & 0xFF;
    unsigned char i;
   
    for(i = 0; i < length; i++) {
        // Intenta la operación de escritura
        i2c_start();
        if(!i2c_write(EEPROM_ADDR)) {
            i2c_stop();
            return;
        }
       
        if(!i2c_write(high_addr)) {
            i2c_stop();
            return;
        }
       
        if(!i2c_write(low_addr)) {
            i2c_stop();
            return;
        }
       
        if(!i2c_write(buffer[i])) {
            i2c_stop();
            return;
        }
       
        i2c_stop();
       
        // Espera a que termine la escritura (tiempo tWR)
        delay_ms(5);
       
        // Incrementa la dirección para el próximo byte
        low_addr++;
        if(low_addr == 0) {
            high_addr++;
        }
    }
}

// Configurar UART a 9600 baudios (para 40 MHz)
void init_uart(void) {
    TRISCbits.TRISC6 = 0;   // TX (RC6) como salida
    TRISCbits.TRISC7 = 1;   // RX (RC7) como entrada
   
    SPBRG = 64;             // 9600 baudios @ 40 MHz
    TXSTAbits.BRGH = 1;     // Alta velocidad
    TXSTAbits.SYNC = 0;     // Modo asíncrono
    RCSTAbits.SPEN = 1;     // Habilitar puerto serial
    TXSTAbits.TXEN = 1;     // Habilitar transmisión
    RCSTAbits.CREN = 1;     // Habilitar recepción continua

// Esperar a que se estabilice
    delay_ms(100);
}   

void putch(char c) {
    while(!PIR1bits.TXIF); // Esperar hasta que el buffer de transmisión esté vacío
    TXREG = c;
}

char getch(void) {
    while(!PIR1bits.RCIF); // Esperar hasta que llegue un dato
    return RCREG;
}

// I2C
void i2c_init(void) {
    SSPCON1 = 0b00101000; // I2C Master, clock = FOSC/(4*(SSPADD+1))
    SSPCON2 = 0;
    SSPADD = ((_XTAL_FREQ/4)/100000)-1; // 100kHz
    SSPSTAT = 0;
    TRISBbits.TRISB0 = 1; // SCL (RB0) como entrada
    TRISBbits.TRISB1 = 1; // SDA (RB1) como entrada
}

void i2c_start(void) {
    SSPCON2bits.SEN = 1;
    while(SSPCON2bits.SEN);
}

void i2c_stop(void) {
    SSPCON2bits.PEN = 1;
    while(SSPCON2bits.PEN);
}

unsigned char i2c_write(unsigned char data) {
    SSPBUF = data;
    while(!PIR1bits.SSPIF);
    PIR1bits.SSPIF = 0;
    return !SSPCON2bits.ACKSTAT; // Retorna 1 si recibió ACK
}

unsigned char i2c_read(unsigned char ack) {
    SSPCON2bits.RCEN = 1;
    while(!SSPSTATbits.BF);
    unsigned char data = SSPBUF;
    SSPCON2bits.ACKDT = !ack;
    SSPCON2bits.ACKEN = 1;
    while(SSPCON2bits.ACKEN);
    return data;
}

// Lectura EEPROM mejorada con manejo de errores
void eeprom_read(unsigned int addr, char *buffer, unsigned char length) {
    unsigned char high_addr = (addr >> 8) & 0xFF;
    unsigned char low_addr = addr & 0xFF;
    unsigned char i;
   
    // Intenta la operación de lectura
    i2c_start();
    if(!i2c_write(EEPROM_ADDR)) {
        i2c_stop();
        strcpy(buffer, "Error: No ACK en direccion EEPROM");
        return;
    }
   
   
    if(!i2c_write(high_addr)) {
        i2c_stop();
        strcpy(buffer, "Error: No ACK en addr alta");
        return;
    }
   
    if(!i2c_write(low_addr)) {
        i2c_stop();
        strcpy(buffer, "Error: No ACK en addr baja");
        return;
    }
   
    i2c_start();
    if(!i2c_write(EEPROM_ADDR | 0x01)) {
        i2c_stop();
        strcpy(buffer, "Error: No ACK en modo lectura");
        return;
    }
   
    for(i = 0; i < length-1; i++) {
        buffer[i] = i2c_read(1);
    }
    buffer[i] = i2c_read(0);
    i2c_stop();
    buffer[length] = '\0';
     
   
   
}

void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 1000; j++);
}

// Enviar cadena por UART
void send_serial(const char *str) {
    while (*str) {
        while (!TXSTAbits.TRMT);
        TXREG = *str++;
    }
}

// Recibir carácter por UART (retorna 0 si no hay dato)
char receive_serial(void) {
    if (PIR1bits.RCIF) {        // Si hay dato recibido
        if (RCSTAbits.OERR) {    // Si hay overrun error
            RCSTAbits.CREN = 0;  // Limpiar error
            RCSTAbits.CREN = 1;  // Rehabilitar recepción
        }
        return RCREG;           // Retornar carácter recibido
    }
    return 0;                   // Retornar 0 si no hay dato
}


  - Tienes que ingresar para ver archivos adjuntos -  


_______________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 16:12:49
Bueno por hoy esta bien aqui dejo el ultimo, control de una pantalla LCD a 4 bits 16x2 :

Código: [Seleccionar]
// EJERC_08_LCD.c
// Control de LCD HD44780 en 4 bits con PIC18F4550
// Cambio entre pantallas con pulsador en RC0

// Compilar con:
// ESTE PROGRAMA EJERC_08.c VER FUNCIONAMIENTO LCD A 4 BITS.
// PARA COMPILAR ESTE PROGRAMA TENEMOS QUE HACER PRIMERO LA COMPILACION DE EJERC_08.c
// Y LUEGO LA COMPILACION DE LCD_4 BITS.c COMO SE MUESTRAN EN LAS DOS INSTRUCCIONES.

// sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=40000000 EJERC_08.c
// sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=40000000 LCD_4BITS.c

// LUEGO SE COMPILADA LOS DOS ARCHIVOS OBJETO AL EJECUTABLE  MAIN.HEX
// sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=40000000 EJERC_08.o LCD_4BITS.o

// TODO EL PROCESO SE PUEDE HACER CON EL SCRIPT ./compila  EN ESTE MISMO DIRECTORIO.


#include <pic18f4550.h>
#include <stdio.h>
#include <string.h>
#include "LCD_4BITS.h"

#pragma config FOSC = HSPLL_HS, PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config PWRT = OFF, BOR = OFF, BORV = 3, VREGEN = OFF
#pragma config STVREN = ON, LVP = OFF, ICPRT = OFF, XINST = OFF, WDT = OFF


// Definición del pin del pulsador
#define PULSADOR PORTCbits.RC0


// Variables globales
unsigned char screen = 0; // 0 = primera pantalla, 1 = segunda pantalla

void main(void) {
    // Configuración de puertos
    TRISB = 0x00;   // Puerto B como salida para el LCD
    TRISCbits.TRISC0 = 1;     // RC0 como entrada para el pulsador
   
    // Inicializar el LCD
    LCD_Init();
   
    // Mostrar mensaje inicial
    LCD_Clear();
    LCD_Set_Cursor(1, 1);
    LCD_Write_String("    Hola    ");
    LCD_Set_Cursor(2, 1);
    LCD_Write_String(" Que tal vamos");
   
    while(1) {
        // Verificar si el pulsador ha sido presionado
        if(PULSADOR == 0) {
            Delay_ms(50); // Debounce
            if(PULSADOR == 0) {
                // Cambiar de pantalla
                screen = !screen;
               
                if(screen == 0) {
                    LCD_Clear();
                    LCD_Set_Cursor(1, 1);
                    LCD_Write_String("    Hola    ");
                    LCD_Set_Cursor(2, 1);
                    LCD_Write_String(" Que tal vamos");
                } else {
                    LCD_Clear();
                    LCD_Set_Cursor(1, 1);
                    LCD_Write_String(" Seguiremos ");
                    LCD_Set_Cursor(2, 1);
                    LCD_Write_String(" aprendiendo ");
                }
               
                // Esperar a que se suelte el pulsador
                while(PULSADOR == 0);
                Delay_ms(50); // Debounce
            }
        }
    }
}




Para compilarlo y grabarlo mediante el siguiente script:

Código: [Seleccionar]
#!/bin/sh
# ESTE PROGRAMA EJERC_08.c VER FUNCIONAMIENTO LCD A 4 BITS.
# PARA COMPILAR ESTE PROGRAMA TENEMOS QUE HACER PRIMERO LA COMPILACION DE EJERC_08.c
# Y LUEGO LA COMPILACION DE LCD_4 BITS.c COMO SE MUESTRAN EN LAS DOS INSTRUCCIONES.

sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=40000000 EJERC_08.c
sleep 0.5

sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=40000000 LCD_4BITS.c
sleep 0.5

# LUEGO SE COMPILADA LOS DOS ARCHIVOS OBJETO AL EJECUTABLE  MAIN.HEX
sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=40000000 EJERC_08.o LCD_4BITS.o




Espero resulte todavia a al alguien interesante.         Saludos.


  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  



______________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)

Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 06 de Septiembre de 2025, 16:37:01
Como comente algunas veces es critico la forma en que estan construidos los programas gputils, pk2cmd y SDCC de instalacion.   

Aqui dejo unos enlaces de paquetes que he construido yo para Debian que a mi me han funcionado.


https://www.mediafire.com/file/bp7qtuqrop7yyvw/sdcc-4.5.0.deb/file (https://www.mediafire.com/file/bp7qtuqrop7yyvw/sdcc-4.5.0.deb/file)

https://www.mediafire.com/file/lebj69vhy27okjp/gputils-1.5.2.x86_64.deb/file (https://www.mediafire.com/file/lebj69vhy27okjp/gputils-1.5.2.x86_64.deb/file)

https://www.mediafire.com/file/hcmspo2igx9yhyc/pk2cmd_1.20.deb/file (https://www.mediafire.com/file/hcmspo2igx9yhyc/pk2cmd_1.20.deb/file)

Saludos.




_____________________________________________________________________________________________________________________________________________________________
Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: sander en 07 de Septiembre de 2025, 13:50:13
A mí me interesa, gracias por compartir   :-/
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 08 de Septiembre de 2025, 08:31:13
Un placer Sander.  Seguire poniendo aqui todos los programas que tengo.

Una vez que te acostrumbras sobre todo a programar en SDCC y en Linux sobre todo, es todo como muy inmediato y rapido.

Aqui dejo otro programa que lo que hace es programar el PIC18F4550 para controlar un LCD 16x2 controlado por I2C atraves del PCF8574A.


He observado que no se han cargado bien los archivos completos asi que aqui dejo un enlace con todos los archvios del proyecto:

https://www.mediafire.com/file/mj8yk0pwnugtlly/EJERC_12.tar.gz/file (https://www.mediafire.com/file/mj8yk0pwnugtlly/EJERC_12.tar.gz/file)


Código: [Seleccionar]
// Configuración del oscilador para 8 MHz con PLL a 48 MHz
#pragma config FOSC = HSPLL_HS // Oscilador HS con PLL habilitado
#pragma config PLLDIV = 2   // 8 MHz / 2 = 4 MHz → PLL x24 = 96 MHz
#pragma config CPUDIV = OSC1_PLL2 // Usa PLL como fuente (CPU = 40 MHz)
#pragma config USBDIV = 2      // Divide PLL para USB (48 MHz)


// Otras configuraciones importantes
#pragma config PWRT = OFF         // Power-up Timer OFF
#pragma config BOR = OFF          // Brown-out Reset OFF
#pragma config BORV = 3           // BOR Voltage Level (3=2.0V)
#pragma config VREGEN = OFF       // Voltage Regulator OFF
#pragma config STVREN = ON        // Stack Overflow Reset ON
#pragma config LVP = OFF       // Low-Voltage Programming deshabilitado
#pragma config ICPRT = OFF        // ICSP Port OFF
#pragma config XINST = OFF        // Extended Instruction Set OFF
#pragma config WDT = OFF       // Watchdog Timer deshabilitado


#include <pic18f4550.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "i2c.h"                        // Libreria del protocolo I2C
#include "lcd_i2c.h"                    // Libreria de la pantalla LCD con modulo I2C
#include "delay.h" 

int v = 1023;
float t = 24.32;
char buffer[20];

const char figura_1[8] = {0x0A, 0x0A, 0x0A, 0x00, 0x11, 0x11, 0x0E, 0x00};
const char figura_2[8] = {0x04, 0x11, 0x0E, 0x04, 0x04, 0x0A, 0x11, 0x00};

void main()
{
    ADCON1bits.PCFG = 0x0F;             // Coloca todos los pines como digitales
    I2C_Init_Master(I2C_100KHZ);        // Inicializa el protocolo i2c
    Lcd_Init();                         // Inicializa la pantalla LCD
   
    Lcd_CGRAM_SetPosition(0);           // Guarda un caracter especial en la posicion 0 de la CGRAM
    Lcd_CGRAM_CreateChar(figura_1);
    Lcd_CGRAM_SetPosition(1);           // Guarda un caracter especial en la posicion 1 de la CGRAM
    Lcd_CGRAM_CreateChar(figura_2);
   
    while(1)
    {
        Lcd_Set_Cursor(1,1);
        Lcd_Write_String("Lcd 16x2 I2C");
        Lcd_Set_Cursor(2,1);
        Lcd_Write_String("Con PIC18F4550");
        Lcd_Set_Cursor(2,15);
        Lcd_Blink();
        delay_ms(2000);
        Lcd_NoBlink();
        Lcd_Clear();
        delay_ms(400);
       
        Lcd_Set_Cursor(1,1);
        Lcd_Write_String("CGRAM Caracteres");
        Lcd_Set_Cursor(2,1);
        Lcd_CGRAM_WriteChar(0);
        Lcd_CGRAM_WriteChar(1);
        delay_ms(1500);
        Lcd_Clear();
        delay_ms(400);
       
        Lcd_Set_Cursor(1,1);
        Lcd_Write_String("Hola a todos");
        delay_ms(500);
        for(char i=0; i<15; i++){
            Lcd_Shift_Right();
            delay_ms(300);
        }
        delay_ms(300);
        for(char i=0; i<15; i++){
            Lcd_Shift_Left();
            delay_ms(300);
        }
        Lcd_Clear();
        delay_ms(400);

    }
}


Necesita varias librerias anexas como se puede ver, que estan creadas aparte de las propias del SDCC y que pongo mas abajo.: 

#include "i2c.h"                        // Libreria del protocolo I2C
#include "lcd_i2c.h"                    // Libreria de la pantalla LCD con modulo I2C
#include "delay.h" 

Para compilar y grabar todos estos archivos, yo creo un script con el siguiente contenido:

Código: [Seleccionar]
#!/bin/sh

sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 EJERC_12.c
sleep .5
sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 i2c.c
sleep .5
sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 lcd_i2c.c
sleep .5
sdcc --use-non-free -mpic16 -p18f4550 -c -D_XTAL_FREQ=48000000 delay.c
sleep .5


# LUEGO SE COMPILADA LOS DOS ARCHIVOS OBJETO AL EJECUTABLE  MAIN.HEX
sdcc --use-non-free -mpic16 -p18f4550 --use-crt=crt0.o -D_XTAL_FREQ=48000000 EJERC_12.o i2c.o lcd_i2c.o delay.o

# con Picsimlab se tiene que simulara a 10 Mhz pero esto puede ser un problema de Picsimlab
# hay que ver con el PIc haber que tal.
sleep 1
pk2cmd -P PIC18F4550 -X -M -F EJERC_12.hex


Esto compilaria el programa y lo grabaria en el PIC.



  - Tienes que ingresar para ver archivos adjuntos -  
  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  
  - Tienes que ingresar para ver archivos adjuntos -  

  - Tienes que ingresar para ver archivos adjuntos -  


Parece que no deja poner mas adjuntos el ultimo adjunto que tenemos que poner con el nombre  delay.h   tendria que tener el texto:

Código: [Seleccionar]
#ifndef DELAY_H
#define DELAY_H

void delay_ms(unsigned int ms);
void delay_us(unsigned int us);

#endif



 
________________________________________________________________________________________________________________________
Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)


Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 08 de Septiembre de 2025, 13:08:18
En este ejercicio podemos ver un PWM con salida en la patita RC2, se le conecta un potenciometro de 10 K a  RA0  variamos el duty-cycle y en RA1 otro potenciometro variamos la frecuencia.

Viene el archivo para cargar la simulacion en PIcsimlab.

Código: [Seleccionar]
#include <pic18f4550.h>
#include <stdint.h>

#define _XTAL_FREQ 40000000
#define F_CPU 40000000UL

#pragma config FOSC = HSPLL_HS, PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config PWRT = OFF, BOR = OFF, BORV = 3, VREGEN = OFF
#pragma config STVREN = ON, LVP = OFF, ICPRT = OFF, XINST = OFF, WDT = OFF

// Prototipos
void ADC_Init();
uint16_t ADC_Read(uint8_t channel);
void PWM_Init();
void PWM_Update(uint16_t duty, uint16_t frequency);
uint16_t map_duty(uint16_t adc_val);
uint16_t map_freq(uint16_t adc_val);
void delay_ms(unsigned int ms);
void delay_us(unsigned int us);

// Variables globales para configuración dinámica del prescaler
uint8_t current_prescaler = 0b10; // Inicia con prescaler 16 (0b10)

void main() {
    ADC_Init();
    PWM_Init();
   
    while(1) {
        uint16_t duty = map_duty(ADC_Read(0));
        uint16_t freq = map_freq(ADC_Read(1));
        PWM_Update(duty, freq);
        delay_ms(10);
    }
}

// Función MAP para duty-cycle (0-1023 → 0-1023)
uint16_t map_duty(uint16_t adc_val) {
    if(adc_val > 1023) adc_val = 1023;
    return adc_val;
}

// Función MAP para frecuencia extendida (0-1023 → 20-20000Hz)
uint16_t map_freq(uint16_t adc_val) {
    if(adc_val >= 1023) return 20000;
    if(adc_val <= 0) return 20;
    return (uint16_t)(20 + (adc_val * 19980UL) / 1023);
}

void PWM_Update(uint16_t duty, uint16_t frequency) {
    // 1. Determinar el mejor prescaler para el rango de frecuencia
    uint8_t new_prescaler;
    if(frequency > 5000) {
        new_prescaler = 0b00; // Prescaler 1 para altas frecuencias
    } else if(frequency > 1000) {
        new_prescaler = 0b01; // Prescaler 4
    } else {
        new_prescaler = 0b10; // Prescaler 16 para bajas frecuencias
    }
   
    // 2. Calcular PR2 con el prescaler seleccionado
    uint8_t prescaler_values[] = {1, 4, 16};
    uint32_t pr2_calc = (_XTAL_FREQ / (4UL * prescaler_values[new_prescaler] * frequency)) - 1;
   
    // 3. Ajustar PR2 dentro de límites (3-255)
    uint8_t pr2_value = (pr2_calc > 255) ? 255 : (pr2_calc < 3) ? 3 : (uint8_t)pr2_calc;
   
    // 4. Solo reconfigurar si cambió el prescaler
    if(new_prescaler != current_prescaler) {
        T2CONbits.TMR2ON = 0;
        T2CONbits.T2CKPS = new_prescaler;
        current_prescaler = new_prescaler;
        T2CONbits.TMR2ON = 1;
    }
   
    // 5. Calcular y actualizar duty cycle
    uint16_t duty_value = ((uint32_t)duty * (pr2_value + 1)) / 256;
    PR2 = pr2_value;
    CCPR1L = duty_value >> 2;
    CCP1CONbits.DC1B = duty_value & 0x03;
}

void ADC_Init() {
    ADCON1 = 0x0D;       // RA0 y RA1 como analógicos
    ADCON2 = 0b10101111; // Justificado derecha, TAD=16, Fosc/32
    ADCON0bits.ADON = 1;
}

uint16_t ADC_Read(uint8_t channel) {
    ADCON0bits.CHS = channel;
    delay_us(20);
    ADCON0bits.GO = 1;
    while(ADCON0bits.GO);
    return (ADRESH << 8) | ADRESL;
}

void PWM_Init() {
    TRISCbits.TRISC2 = 0;
    CCP1CON = 0x0C;      // Modo PWM
    T2CON = 0x04;        // Prescaler 16 inicial
    PR2 = 124;           // Valor inicial (~1kHz)
    CCPR1L = 0;
    CCP1CONbits.DC1B = 0;
    TMR2 = 0;
    T2CONbits.TMR2ON = 1;
}

// Funciones delay originales
void delay_ms(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++) {
        for(j = 0; j < 1000; j++);
    }
}

void delay_us(unsigned int us) {
    unsigned int i;
    for(i = 0; i < us; i++);
}


He comprimido todo el directorio con simulacion Picsimlab, video y demas archivos y se pued bajar:


https://www.mediafire.com/file/aortl88h2xkbzp8/EJERC_11.tar.gz/file (https://www.mediafire.com/file/aortl88h2xkbzp8/EJERC_11.tar.gz/file)

Saludos.


_____________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 09 de Septiembre de 2025, 10:52:11
Aqui dejo un programa para controlar el display de 4 digitos, el TM1637 controlado por bus SPI.  Dejo el archivo comprimido EJERC_24.tar conteniendo todos los archivos para la programacion.

  - Tienes que ingresar para ver archivos adjuntos -  

Saludos.



______________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 09 de Septiembre de 2025, 18:54:08
Aqui dejo un programa completo de control panel I2C - LCD 16x2 con el  PCF8574, y contador de ancho de pulso por el modulo capture con interrupcion, midiendo el ancho de pulso y calculando la frecuencia y mostrandolo por el LCD.

La frecuencia de onda cuadrada se le mete por la patita RC2  yo le pongo una resistencia de 1K en serie con un nivel de señal de 5 voltios.  El panel  Lcd se controla por RB0 (SDA) y RB1(SCL).
y luego la patitas de alimentacion GND y VCC.

El programa no esta calibrado y hay que calibrarlo para que de la frecuencia exacta.

En el siguiete post i dejo el archivo comprimido con todos los archivos necesario, por que no me lo deja meter todo en un post.


Aqui una imagen de su funcionamiento.

(http://[attachment id=1 msg=427058][/attachment])


Saludos.


_________________________________________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 09 de Septiembre de 2025, 19:10:46
Aqui esta el archivo del anterior post :

  - Tienes que ingresar para ver archivos adjuntos -  

Saludos.


____________________________________________________________________________________________________________________________________________________________
Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 10 de Septiembre de 2025, 18:42:06
El siguiente ejercicio es para manejar un PIR de deteccion de movimiento, como los que hay para arduino, en la  foto contenida en el archivo EJERC_25.7z se puede apreciar la que es y el conexionado.

El PIR la patita de salida se conecta a RB0 y hay un led conectado a RA5 que cuando de se activa el PIR se enciende.

El programa no tiene gran cosa, aqui dejo el archivo comprimido con todos los archivos necesarios. 

Al ejecutar el script  ./compilar   se compila el programa y se graba en la memoria flash del PIC18F4550.

  - Tienes que ingresar para ver archivos adjuntos -  


Saludos.
__________________________________________________________________________________________________________________________

Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 11 de Septiembre de 2025, 15:25:30
Este ejercicio es para ver el PWM a la salida de la patita RC2,   con el potenciometro de 10 k o 5 K conectado a RA0 pordemos variar el Duty-cycle y con el potenciometro en RA1 de mismos valores podemos variar la frecuencia. Dentro del  archivo del directorio comprimido podemos ver la simulacion con PIcsimlab y todos los archivos.

Para compilarlo y programar el Pic con  ./compilar

  - Tienes que ingresar para ver archivos adjuntos -  

___________________________________________________________________________________________________________________________________________________________
Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)
Título: Re:Programacion de Pic con lenguaje de programacion SDCC.
Publicado por: misterweb en 11 de Septiembre de 2025, 15:34:06
Un lanza en favor de todos estos lenguajes rusticos como SDCC es que uno pensando en como fabricantes de software nos ofrecen software gratuitos de 1, 2 y mas Gb. Y a veces piensa uno, todo estos progragramas tan bonitos creados por estas plataformas, en los cuales algunas veces hemos dejado las pestañas creando nuestros programas, no sera que acaben en algunas de estas plataformas... Quien sabra?

Bueno pienso que el que quiera empezar a jugar con este metodo de programacion tiene un buen comienzo,  me tomo un descanso... ya seguire poniendo mas programas.

Saludos.

_______________________________________________________________________________________________________________________________________________________
Telgram:

https://t.me/electronica_puppy (https://t.me/electronica_puppy)