TODOPIC
Microcontroladores PIC => Lenguaje C para microcontroladores PIC => Mensaje iniciado 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.
// 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)
-
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
#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)
-
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:
#!/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 :
#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 :
#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)
-
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:
// 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
#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.
#!/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)
-
El siguiente programa para manejar la pantalla del nokia5110.
// 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
#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:
#!/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)
-
El siguiente programa para grabar datos en una memoria Eeprom por I2C y luego leerla.
// 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)
-
Bueno por hoy esta bien aqui dejo el ultimo, control de una pantalla LCD a 4 bits 16x2 :
// 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:
#!/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)
-
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)
-
A mí me interesa, gracias por compartir :-/
-
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)
// 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:
#!/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:
#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)
-
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.
#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)
-
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)
-
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)
-
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)
-
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)
-
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)
-
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)