Este repositorio es un almacen que tiene guardado todos los proyectos y trabajos hechos para sistemas embebidos, porgramados en c, en el IDE de Microchip.
Este repositorio está dedicado a almacenar y compartir los trabajos, proyectos y ejercicios realizados como parte del programa de desarrollo de sistemas embebidos utilizando el microcontrolador dsPIC33FJ128MC802 de Microchip. Los archivos presentes aquí representan el resultado del esfuerzo en equipo de estudiantes que han trabajado en diversas pruebas y estudios para desarrollar programas específicamente diseñados para este microcontrolador.
El sistema embebido al que se refiere este repositorio se centra en el uso del microcontrolador dsPIC33FJ128MC802 de Microchip. Este microcontrolador es una unidad de procesamiento altamente integrada diseñada para aplicaciones embebidas que requieren procesamiento de señales digitales en tiempo real, como controladores de motores, dispositivos de comunicación y sistemas de control en general. El programa de desarrollo incluye actividades de diseño, programación y prueba de software específicamente adaptado para este microcontrolador, permitiendo a los estudiantes explorar y aplicar conceptos relacionados con el desarrollo de sistemas embebidos en un entorno educativo o profesional.
El repositorio está organizado en las siguientes secciones:
- Tareas de MPEI: Ejercicios y problemas asignados como tarea en clase relacionados con Métodos Probabilísticos para la Ingeniería Informática.
- Proyectos de Microprocesadores e Interfaces: Proyectos relacionados con el desarrollo de sistemas embebidos utilizando el microcontrolador dsPIC33FJ128MC802 y sus interfaces.
- Ejemplos de Código: Ejemplos de código para entender el funcionamiento del microcontrolador y sus periféricos.
- Documentación: Documentación técnica, manuales y notas de clase relacionadas con MPEI y el desarrollo de sistemas embebidos.
- Recursos: Enlaces útiles, bibliografía recomendada y material adicional para profundizar en los temas tratados.
Este proyecto trata de esplicar la simplicidad del sistema de entorno de desarrollo integrado (IDE) de MPLAB. Además, la implementación, la inicialización y la configuración del microcontrolador DSPIC33FJ128MC802.
#pragma config FNOSC = FRC // oscillator mode
#pragma config IESO = ON // internal external switch
#pragma config FWDTEN = OFF // watchdog timer enable
#pragma config ICS = PGD3 // comm channel select
Configuración inicial del micro
#include "xc.h"
#include <p33FJ128MC802.h>
#define FCY 7370000UL // reloj micro
#include <libpic30.h>
Configuración del micro con su reloj y libreria del delay
#define x LATXbits.LATA0 // X es A o B
#define y PORTXbits.RB0 //
AD1PCFGL = 0xFFFF; // pines analogos
// registos de 1x16 bits digital 1 analogo 0
TRISX = 0x0000; // puerto X / entrada 1 salida 0
Configuración pines de IN/OUT Configuración de periféricos
Se usan las tablas del datasheet para reprogramar pines.
RPINRXXbits.U2RXR = 13; // periferico UART2 como entrada en el pin RP13
RPORYbits.RP15R = 5; // periferico UART como salida en el pin RP15
lo anterior es un ejemplo, pero para hacer la reprogramación de pines se necesita de la siguientes tablas.
Aqui entra el uso de las interrupciones.
IFS0bits.INT0IF = 0; // clarear interrupción
IEC0bits.INT0IE = 1; // activar interrupción
void __attribute__((interrupt, auto_psv)) _INT0Interrupt(void) {
// código
IFS0bits.INT0IF = 0; // bandera para desactivar interrupción
}
}
A partir de un teclado de matriz se hace una acción matematica:
{ '1', '2', '3', 'A' }, { '4', '5', '6', 'B' }, { '7', '8', '9', 'C' }, { '*', '0', '#', 'D' }
int calcular(int a, int b, int operacion) {
switch (operacion) {
case 10: // es suma
return a + b;
case 11: // es resta
return a - b;
case 12: // es multiplicacion
return a * b;
case 13: // es divicion
if (b != 0) {
return a / b;
} else {
// Manejar el error de división por cero
return 0;
}
default:
// Manejar el caso de operación no válida
return 0;
}
}
Se implementa y configura el conversor análogo digital para adquirir una variación de voltaje. Este microcontrolador tiene 6 entradas analógicas (AN0-AN5).
Ahora, El SAR o sucessive aproximation register, es el responsable de identificar el valor de la señal de entrada analogica. La señal analógica de entrada (Vin) se muestrea y se mantiene a un valor constante. Esto se hace mediante un circuito de muestreo y retención (S/H). Un convertidor digital-analógico (DAC) interno genera una señal analógica que se compara con la señal de entrada muestreada. Un comparador determina si la señal del DAC es mayor o menor que la señal de entrada. El resultado de la comparación se utiliza para controlar un registro de aproximaciones sucesivas (SAR). El registro SAR se desplaza un bit a la derecha en cada ciclo de comparación. El proceso de comparación y desplazamiento continúa hasta que el valor en el registro SAR sea igual o mayor que la señal de entrada. El valor final en el registro SAR es la representación digital de la señal analógica de entrada.
El conversor análogo-digital tiene como formatos de salida: con/sin signo fraccional y entero.
Configure el módulo ADC:
- Seleccione los pines del puerto como entradas analógicas (AD1PCFGH<15:0> ο AD1PCFGL<15:0>).
- Seleccione la fuente de referencia de voltaje para que coincida rango esperado en entradas analógicas (AD1CON2<15:13>)
- Seleccione el reloj de conversión analógica para hacer coincidir la velocidad de datos deseada con el procesador reloj (AD1CON3<7:0>)
- Determinar cuántos canales S/H se utilizan (AD1CON2<9:8> y AD1PCFGH<15:0> ó AD1PCFGL<15:0>)
- Seleccionar la muestra/conversión adecuada secuencia (AD1CON1<7:5> y AD1CON3<12:8>)
- Seleccionar cómo son los resultados de conversión presentado en el búfer (AD1CON1<9:8>)
- Encienda el módulo ADC (AD1CON1<15>)
Se configura el ADC con una función para generalizar con cada registro que indica el datasheet.
void ADC_conf (){
// Se establecen los registros de control del conversor ADC
// AD1CON1: Registro de control 1 del ADC
AD1CON1 = 0X0000; // Se limpian todos los bits del registro para configurarlos según sea necesario
// AD1CON2: Registro de control 2 del ADC
AD1CON2 = 0; // Se limpian todos los bits del registro
// AD1CON3: Registro de control 3 del ADC
AD1CON3 = 0X0000; // Se limpian todos los bits del registro
// AD1CSSL: Registro de selección de entrada analógica escaneada
AD1CSSL = 0; // Se establecen todos los bits en 0 para omitir el escaneo de las entradas analógicas
// AD1CHS0: Registro de selección de canal de entrada analógica
AD1CHS0 = 0X0000; // Se limpian todos los bits del registro
// Se activa el módulo ADC para que pueda operar
AD1CON1bits.ADON = 1;
// Se configura el módulo ADC para utilizar el reloj interno RC para la conversión
AD1CON3bits.ADRC = 1;
// Se selecciona el canal de entrada positivo AN2 para el canal 0 del ADC
AD1CHS0bits.CH0SA = 2;
// Se selecciona el canal de entrada positivo AN2 para el canal 0 del ADC (solo en caso de conversor de 10 bits)
AD1CHS0bits.CH0SB = 2;
}
Se configura la adquisición de la variación de voltaje que obtiene el PIN AN del micro.
void adquirir_AD() {
// Se inicia la conversión ADC colocando el bit SAMP en 1
AD1CON1bits.SAMP = 1;
// Se espera un corto período de tiempo para permitir que el muestreo ocurra
// Esto permite que la señal de entrada alcance su estado estable antes de iniciar la conversión
__delay_ms(1);
// Se detiene el muestreo colocando el bit SAMP en 0, lo que inicia la conversión ADC
AD1CON1bits.SAMP = 0;
// Se espera hasta que la conversión ADC esté completa, indicado por el bit DONE en 1
while (!AD1CON1bits.DONE);
// Se lee el resultado de la conversión ADC desde el registro ADCBUF0
// En este caso, se está leyendo el valor de la conversión directamente, sin aplicar desplazamiento o máscara
// El valor leído representará el voltaje medido en el canal ADC configurado previamente
lectura = ADCBUF0;
// Se introduce un pequeño retardo antes de salir de la función
__delay_ms(1);
}
La linea while (!AD1CON1bits.DONE);, especifica que se esta esperando a que termine de hacer el proceso de conversión para seguir con el siguiente proceso. Tambien se destaca que se puede usar voltajes externos como referencia cambiando los valores del registro VCFG del control 2 del ADC.
Además, se permite hacer un muestreo de datos por medio de 4 canales. Esto quiere decir que se puede hacer la conversión simultanea para 4 entradas analógica, dando la señal de salida en los registros respectivos de AD1BUFX de 12 o 10 bits dependiendo la configuración.
Se implementa un ejemplo de visualizador dinámico para el micro, sin necesidad de decodificador con 2 displays 7 segmentos.
La UART funciona en un modo asíncrono, lo que significa que no hay un reloj de sincronización compartido entre el transmisor y el receptor. En su lugar, los datos se envían en serie, bit por bit, junto con una señal de inicio y posiblemente una señal de parada para delimitar cada byte de datos. La velocidad de transmisión de datos, conocida como baud rate, se configura para asegurar que el receptor pueda interpretar correctamente los bits recibidos.
La UART consta de dos partes principales: el transmisor y el receptor. El transmisor toma datos paralelos desde el microcontrolador y los convierte en una secuencia serial de bits que se envían a través de un solo cable. El receptor, por otro lado, toma la secuencia de bits serial recibida a través del cable y la convierte de nuevo en datos paralelos que pueden ser utilizados por el microcontrolador.
Se utiliza un conversor usb-serial (ft232). Y por ende se especifica el uso de baudios(bps) que describe la velocidad de transmición de datos.
configuración del modulo UART
- Inicializar el registro UxBRG un valor de baudio apropiado.
- Colocar tamaño del dato UxMODEbits.PDSEL, colocar cantidad bits de stop UxMODEbits.STSEL.
- Colocar de ser así la interrupción IEC0bits.U1XTXIE 0 para clarearla y 1 para iniciarla.
- Especificar la prioridad de la interrupción IPC0.U1TXIP.
- activar uart UxMODEbits.UARTEN.
- activar la transmicion UxMODEbits.UTXEN
void UART_conf() {
// Se configuran los registros del módulo UART
// U1MODE: Registro de modo del módulo UART
U1MODEbits.STSEL = 0; // Selecciona 1 bit de parada
U1MODEbits.PDSEL = 0; // Configura sin paridad y 8 bits de datos
U1MODEbits.ABAUD = 0; // Deshabilita el modo de detección automática de baud rate
U1MODEbits.BRGH = 0; // Establece el modo de velocidad estándar
// U1BRG: Registro de generador de baud rate del módulo UART
U1BRG = BRGVAL; // Establece el valor del generador de baud rate para la velocidad deseada
// U1STAbits: Registro de estado del módulo UART
U1STAbits.UTXISEL1 = 0; // Interrupción después de recibir un carácter RX
// IEC0bits: Registro de habilitación de interrupciones del módulo UART
IEC0bits.U1RXIE = 1; // Habilita la interrupción de recepción UART
// IPC3bits: Registro de prioridad de interrupción de transmisión del módulo UART
IPC3bits.U1TXIP = 5; // Establece la prioridad de interrupción de transmisión (menor que la de recepción)
// IPC2bits: Registro de prioridad de interrupción de recepción del módulo UART
IPC2bits.U1RXIP = 6; // Establece la prioridad de interrupción de recepción (mayor que la de transmisión)
// Se activa el módulo UART y se habilita la transmisión
U1MODEbits.UARTEN = 1;
U1STAbits.UTXEN = 1;
}
Aquí se expecifica de igual manera la forma en que se debe configurar la uart en transmición y en recepción.
Es un contador y/o temporizador por flancos de reloj o de tiempo de instrucción. Existen 4 tipos (temporizador, comparador, contador sincrono y asincrono).
Configuración del Timer.
- Decidimos que timer usar // 5 timers // 1 2/3 4/5
- Decidimos si es de 16 o 32 bits
- Se decide el modo de operación
- Se coloca el prescaler del contador 1: H = 1.8.64.256
- Definir el límite de bits
- Definir qué hará el timer
void conf_timer() {
// T1CONbits: Registro de control del Timer 1
// TCKPS: Bits de prescaler del Timer 1
T1CONbits.TCKPS = 3; // Configura el preescalador del Timer 1 a 1:256
// TGATE: Bit de control de sincronización del Timer 1
T1CONbits.TGATE = 0; // Deshabilita la sincronización del Timer 1
// TCS: Bit de selección de fuente de reloj del Timer 1
T1CONbits.TCS = 0; // Selecciona el reloj interno Fosc/2 como fuente de reloj para el Timer 1
// PR1: Registro de periodo del Timer 1
PR1 = 0xFFFF; // Establece el valor máximo de cuenta para el Timer 1 (16 bits)
// TON: Bit de encendido/apagado del Timer 1
T1CONbits.TON = 1; // Habilita el Timer 1
}
Ahora, una demostración tipo parcial paso a paso:
yn=241; // número 241 decimal 1111 0001 binario F1 hexa
yn=yn>>1; // desplaza a la derecha 1 bit 0111 1000 binario 120 decimal 78 hexa
resto=yn%100; // saca el 120 mod 100 = 20
yn=yn/100; // saca el 120 div 100 = 1
U1TXREG=yn+0x0030; // 1 + 48 = 49 -> 1
yn=resto; // 20
resto=yn%10; // saca el 20 mod 10 = 0
yn=yn/10; // saca el 20 div 10 = 2
U1TXREG=yn+0x0030; // 2 + 48 = 50 -> 2
U1TXREG=resto+0x0030; // 0 + 48 = 49 -> 0
U1TXREG='\n'; // end line
dejando al final en el terminal de arduino ide un 120\endl
Entonces si fuera un reloj, si es de 16 bits: 2/FCY * 2^16 * prescaler = Tiempo total en segundos.
Pulse width modulation
El microcontrolador trae 8 pines de salida pwm y solo 6 son salidas sincronisables. Deben definirse en las salidas de TRIS. Tiene salidas H(high) y L(low).
void conf_pwm(){
P2TCONbits.PTCKPS = 1; // periodo base prescaler de tiempo T = 4Tcy
P2TCONbits.PTMOD = 0; // pwm opera en modo free running
P2TMRbits.PTMR = 0; //
P2TPERbits.PTPER = 0x4800; // tiempo T = 20ms 20/2*Tcy = 18432
PWM2CON1bits.PMOD1 = 0; // habilita pin H y L para ser complementarios
PWM2CON1bits.PEN1H = 1; // se habilitan pin pwm H
PWM2CON1bits.PEN1L = 1; // se habilita pin pwm L
P2DTCON1bits.DTAPS = 0; // prescalador Tiempo muerto de pwm
P2DTCON1bits.DTA = 59; // tiempo muerto 4us
P2DC1 = 0x0733; // Duty Cycle 4800=50% 2ms : E66 = 20% 1ms : 733 = 10% 480=6.25% 64C = 8.75% = xms/20ms * 18432 * 2
P2TCONbits.PTEN = 1; // enable PWM timerbase
}
Si el la frecuencia del microcontrolador es 7372800 entonces el periodo del micro es Fcy = Fosc/2 = 3686400 el periodo Tcy = 0.2712 673611 us Si se quiere cierto periodo del PWM T con o sin prescaler T/Tcy = número -> PTPER
si T = 20ms n = T/Tcy = 20ms / 0.2712us = 73746 ó n = T/4Tcy = 20ms/40.2712us = 18436 debe ser de 0x0000 en hex o 0000 0000 0000 0000 bits PTPER = 18436 = 0x4804 que es el registro con el periodo deseado
si se necesita 50% duty cycle entonces PxDC1 = 0x4804 = 18436 -> 10ms que se ve como el 100% en high si se necesita 25% duty cycle entonces PxDC1 = 0x2402 = 18436/2 = 9218 -> 5ms que se ve como el 50% en high
si se necesita 1ms de duty cycle entonces si 18436 -> 10ms ent x -> 1ms --> 1ms18436/10ms = 1843 PxDC1 = 0x733 = 18436/10 = 1843 si se necesita 1.5ms de duty cycle entonces si 18436 -> 10ms entx -> 1.5ms --> 1.5ms18436/10ms = 2765 PxDC1 = 0xACD = 18436/6.66 = 2765
https://robots-argentina.com.ar/didactica/control-de-motores-de-corriente-continua-con-puente-h/
El sistema pwm es una técnica utilizada para controlar la cantidad de potencia entregada a una carga, como un motor, una bombilla LED o un servo motor, mediante la variación del ancho del pulso de una señal digital.
DUTY CYCLE = pulse width high / total period * 100%
El driver que se utilizará en el curso es el L298N:
Vpromedio = (Vmax - Vmin) * DUTY CYCLE / 100