Paso 2: Software
Este paso iremos a través de cada parte del software y explicar lo que hace. Si no tienen ningún interés en modificar el software y quiero construir la cosa, usted puede ir al siguiente paso. El archivo hexadecimal siempre hará que el proyecto funciona como diseñado.
Configureation básico de periferials de chip
#pragma config OSC = INTIO2, WDT = OFF, LVP = OFF
#include < p18f1320.h >
Esto define el oscilador interno y carga la configuración de la 18F1320, si desea utilizar un pic diferente que esta es una de las cosas que tienes que cambiar
pines de configuración para la entrada PWM
#define ReceiverPin PORTBbits.RB3
#define ReceiverTris TRISBbits.TRISB3
Esto es solo dar un nombre al pin para recibir la señal por lo que es más claro hablar de.
PWM captura de variables
unsigned int PWM1RiseTime = 0; valor del temporizador en el borde de levantamiento de captura
unsigned int PWM1FallTime = 0; valor del temporizador de captura de borde descendente
unsigned int PWM1Width = 0; ancho calculado
unsigned int CH1_width = 0;
unsigned int CH2_width = 0;
unsigned int CH3_width = 0;
unsigned int PWMGap = 0; distancia calculada entre pulsos
char PWM1Edge = 1; borde actualmente monitoreados 1 = aumento, 0 = caer
variables de botón
unsigned char button_1_debounce_wait = 0;
Puesto que la señal PWM del receptor se comunica por enviar impulsos de diferentes variables de la anchura se declaran para sostener estas anchuras, una vez que se calculan.
unsigned char calibration_mode = 0;
#define mode_operate 0
#define mode_CH1_high 1
#define mode_CH1_med 2
#define mode_CH1_low 3
#define mode_CH2_high 4
#define mode_CH2_med 5
#define mode_CH2_low 6
unsigned int limit_CH1_high = 2381;
unsigned int limit_CH1_med = 3307;
unsigned int limit_CH1_low = 4286;
unsigned int limit_CH2_high = 2022;
unsigned int limit_CH2_med = 2946;
unsigned int limit_CH2_low = 3983;
unsigned int CH_temp = 0;
Cuando el modo de calibración está en el sistema se reajuste estos "límites" para adaptarse a la señal. Estos son los valores por defecto, el funcionamiento de la calibración se explicará más adelante.
variables de control del motor
#define Motor_PWM_Rez 16 //number si diferentes velocidades posible hacia adelante y hacia atrás
#define center_buffer 20 //this es la fracción de la gama antes de movimiento
Son constantes que se puede ajustar si está utilizando diversas partes. El búfer de centro es la zona muerta en el centro donde el controlador no hace que el motor do cualquier cosa. El rezolution es cuántos diferentes velocidades el sistema dividirá gama de control en.
unsigned char Motor_Phase = 0; //as ciclos esto a tiempo los motores
unsigned int CH1_range = 2000;
unsigned char Motor_A_Speed = 0; Esta es la velocidad del motor A, % 100 será igual a la rezolution
unsigned char CH1_forward_increment = 10; //the ancho del rango para cada salida de velocidad
unsigned char CH1_reverse_increment = 10;
unsigned int CH2_range = 2000;
unsigned char Motor_B_Speed = 0; Esta es la velocidad del motor A, % 100 será igual a la rezolution
unsigned char CH2_forward_increment = 10; //the ancho del rango para cada salida de velocidad
unsigned char CH2_reverse_increment = 10;
typedef struct
{
signo de motor_A_Direction: 1;
signo de motor_B_Direction: 1;
signo de button_1_last_state: 1;
} PEDACITOS DE;
unsigned char motor_A_inverted = 1; //this relacionados con la calibración
unsigned char motor_B_inverted = 1;
unsigned char motor_calibration_needed = 1;
Bits de BITS volátiles;
variables de tiempo
unsigned char slow_count = 0; Esto se utiliza para crear el escalado temporizador para eventos más lento
La variable anterior será un contador para que una subdivisión de la interrupción de temporizador puede ir sólo cada uno de muchos pasos de temporizador.
configurar interrupción
void low_ISR (void); //prototype
#pragma código low_vector = 0x08 //0X08 es poco 0X18 es alta
void low_interrupt (void) {}
_asm goto low_ISR _endasm
}
código #pragma
#pragma interrupción low_ISR
Esta parte no es la interrupción en sí mismo pero arriba del conjunto para la interrupción que se produzca. La interrupción es un evento que permite algo que se puede activar para que el programa no tiene que ser un gran bucle.
void main
{
OSCCON = 0X72; Reloj de 8MHz
mientras (!. OSCCONbits.IOFS); Esperar a OSC para convertirse en estable
configura timer1
PIR1bits.TMR1IF = 0; borra la bandera de cierre de temporizador 1
T1CONbits.TMR1ON = 1; Encienda el temporizador
T1CONbits.T1CKPS1 = 0; sistema divisor
T1CONbits.T1CKPS0 = 0; sistema divisor
configuración timer2
PIR1bits.TMR2IF = 0; borra la bandera de cierre de temporizador 2
PIE1bits.TMR2IE = 1; habilitar la interrupción
PR2 = 199;
T2CON = 0b00000100; (-) siempre 0 postscale (-) (-) (-) prescaler de encendido-apagado
configurar el CCP1
CCP1CON = 0b0000101; configurar el CCP1 para captura, borde de levantamiento
INTCONbits.PEIE=1; Permitir interrupciones periféricas
PIE1bits.CCP1IE=1; habilita interrupción del CCP1
INTCONbits.GIE=1; permitir la ramificación interrumpir
ReceiverTris = 1; establecer RB3 entrada así que puede trabajar la captura.
TRISBbits.TRISB2 = 1; establecer rb2 para por lo que puede ser utilizado para diferenciar canales
El módulo de captura hace todo el pesado trabajo aquí. Por encima se inicializa para esperar la señal para levantarse, más adelante esto cambiará dinámicamente para captura de anchura de pulso.
configurar puertos
ADCON1 = 0XFF; todo digital
INTCON2bits.RBPU = 0; Puerto b pullups débil en
Estos serán salidas de motor
TRISAbits.TRISA0 = 0;
#define Motor_Pin_A1 LATAbits.LATA0
TRISAbits.TRISA1 = 0;
#define Motor_Pin_A2 LATAbits.LATA1
TRISAbits.TRISA2 = 0;
#define Motor_Pin_B1 LATAbits.LATA2
TRISAbits.TRISA3 = 0;
#define Motor_Pin_B2 LATAbits.LATA3
Estos comandos configurar los pines necesarios para controlar los motores actúan como salidas. Luego los pernos de motor llevan el nombre de fácil acceso.
Estos serán salidas de indicador
TRISAbits.TRISA6 = 0;
TRISAbits.TRISA7 = 0;
se trata de la entrada de señal del servo
TRISBbits.TRISB0 = 1;
inicialmente calibrar las gamas RC
motor_calibration_needed = 1;
while(1)
{
}
}
Mientras bucles mantiene el programa de terminación. No se deje engañar por el hecho de que está vacía. Interrupciones activarán eventos y el reloj sigue funcionando.
A continuación es el inicio de la interrupción de temporizador. Apaga periódicamente a la máxima velocidad que cualquier función requiere, para operaciones rápidas como decidir si es momento de apagar el motor, entonces se subdivide con contadores para operar las funciones que no requieren de tan alta velocidad, tales como control de la entrada y decidir si debe cambiar la velocidad.
void low_ISR(void)
{
Bandera de temporizador 2 (actualmente programado para interrumpir a 10Khz)
if(PIR1bits.TMR2IF == 1)
{
PIR1bits.TMR2IF = 0; borra la bandera de cierre de temporizador 1
Así que en cuanto a no perder el tiempo haciendo cosas más rápidamente que sea necesario (una buena manera de mirar muchos tipos de trabajo) la parte de abajo usa la variable "slow_count" sólo ejecutar cada 100 veces que el bucle exterior se ejecuta.
Esta función se ejecuta en 100 Hz *** withEN
slow_count ++;
if(slow_count > 100)
{
slow_count = 1; //reset cuenta para la próxima vez
Botón de calibración del mango
if(button_1_debounce_wait > 0) {button_1_debounce_wait--;}
if(PORTBbits.RB0 == 0) {}
Si (Bits.button_1_last_state == 0 & & button_1_debounce_wait == 0) //button solo prensado
{
button_1_debounce_wait = 10; //set debounce cuenta
calibration_mode ++;
if(calibration_mode > 6) {calibration_mode = 0;}
}
Bits.button_1_last_state = 1;
}
otra cosa
{
Bits.button_1_last_state = 0;
}
extremo del botón de calibración
A continuación la calibración se aplica realmente. Esto se hace en el modo de operación normal por lo que las luces se apagan ambos. El programa comprueba si el intervalo de calibración es al revés, alta es menor que bajo y viceversa y si así establece una marca para que las direcciones de los motores actuará en consecuencia.
Manejar indicadores Led modo
if(calibration_mode == mode_operate)
{
LATAbits.LATA6 = 0;
LATAbits.LATA7 = 0;
if(motor_calibration_needed == 1)
{
motor_calibration_needed = 0; claro la bandera
calcular las variables de calibración para CH1
if(limit_CH1_low < limit_CH1_high) //speed aumenta a medida que aumenta de número
{
motor_A_inverted = 0;
}
Else / / disminuye la velocidad aumenta como número
{
tan alto es el mayor valor de intercambio
CH_temp = limit_CH1_low;
limit_CH1_low = limit_CH1_high;
limit_CH1_high = CH_temp;
motor_A_inverted = 1;
}
CH1_range = limit_CH1_high-limit_CH1_low;
CH1_forward_increment = (limit_CH1_high-limit_CH1_med-((limit_CH1_high-limit_CH1_med)/center_buffer)) / Motor_PWM_Rez;
CH1_reverse_increment = (limit_CH1_med-limit_CH1_low-((limit_CH1_med-limit_CH1_low)/center_buffer)) / Motor_PWM_Rez;
}
calcular las variables de calibración para CH2
if(limit_CH2_low < limit_CH2_high) //speed aumenta a medida que aumenta de número
{
motor_B_inverted = 0;
}
Else / / disminuye la velocidad aumenta como número
{
tan alto es el mayor valor de intercambio
CH_temp = limit_CH2_low;
limit_CH2_low = limit_CH2_high;
limit_CH2_high = CH_temp;
motor_B_inverted = 1;
}
CH2_range = limit_CH2_high-limit_CH2_low;
CH2_forward_increment = (limit_CH2_high-limit_CH2_med-((limit_CH2_high-limit_CH2_med)/center_buffer)) / Motor_PWM_Rez;
CH2_reverse_increment = (limit_CH2_med-limit_CH2_low-((limit_CH2_med-limit_CH2_low)/center_buffer)) / Motor_PWM_Rez;
}
final de indicadores led modo
Debajo de calibración se maneja. Cada vez que el botón se presiona a los cambios del modo de calibración, indicando que se establece un nuevo límite. El patrón es CH1 completo adelante, medias descanso, completo hacia atrás, entonces el mismo tres posiciones nuevamente en el canal dos. Los indicadores muestran para no en modo de calibración, una en para el avance, el otro para atrás y ambos de medio punto de descanso. No es una interfaz sólida pero hace el trabajo.
calibración
if(calibration_mode == mode_CH1_high)
{
Todas estas cosas LATA están sólo la luz se enciende para indicar el modo del usuario. Como se puede ver realmente no establece los límites cuando se pulsa el botón. Sólo coloca en cualquier lugar están en ese modo, así cuando usted empuja el botón otra vez y ese modo termina, que se mantiene el punto de calibración.
LATAbits.LATA6 = 0;
LATAbits.LATA7 = 1;
limit_CH1_high = CH1_width;
}
if(calibration_mode == mode_CH1_med)
{
LATAbits.LATA6 = 1;
LATAbits.LATA7 = 1;
limit_CH1_med = CH1_width;
}
if(calibration_mode == mode_CH1_low)
{
LATAbits.LATA6 = 1;
LATAbits.LATA7 = 0;
limit_CH1_low = CH1_width;
}
if(calibration_mode == mode_CH2_high)
{
LATAbits.LATA6 = 0;
LATAbits.LATA7 = 1;
limit_CH2_high = CH2_width;
}
if(calibration_mode == mode_CH2_med)
{
LATAbits.LATA6 = 1;
LATAbits.LATA7 = 1;
limit_CH2_med = CH2_width;
}
if(calibration_mode == mode_CH2_low)
{
LATAbits.LATA6 = 1;
LATAbits.LATA7 = 0;
limit_CH2_low = CH2_width;
motor_calibration_needed = 1;
}
Ahora las velocidades del motor necesitan ser calculado. La ecuación obtiene el ancho del pulso para ese motor, decide si está sobre el punto medio o no para decidir la dirección, entonces encuentra tiene rango utilizando la resolución de control del motor dentro de la gama total de anchuras posibles.
calcular la velocidad a del motor
Motor_A_Speed = 0;
if(CH1_width > limit_CH1_med+((limit_CH1_high-limit_CH1_med)/center_buffer)) gama de //upper
{
Motor_A_Speed = (CH1_width-limit_CH1_med-((limit_CH1_high-limit_CH1_med)/center_buffer)) / CH1_forward_increment;
Bits.motor_A_Direction = motor_A_inverted;
}
if(CH1_width < limit_CH1_med-((limit_CH1_med-limit_CH1_low)/center_buffer)) gama de //lower
{
Motor_A_Speed = (limit_CH1_med-CH1_width-((limit_CH1_med-limit_CH1_low)/center_buffer)) / CH1_reverse_increment;
Bits.motor_A_Direction =! motor_A_inverted;
}
calcular la velocidad del motor B
Motor_B_Speed = 0;
if(CH2_width > limit_CH2_med+((limit_CH2_high-limit_CH2_med)/center_buffer)) gama de //upper
{
Motor_B_Speed = (CH2_width-limit_CH2_med-((limit_CH2_high-limit_CH2_med)/center_buffer)) / CH2_forward_increment;
Bits.motor_B_Direction = motor_B_inverted;
}
if(CH2_width < limit_CH2_med-((limit_CH2_med-limit_CH2_low)/center_buffer)) gama de //lower
{
Motor_B_Speed = (limit_CH2_med-CH2_width-((limit_CH2_med-limit_CH2_low)/center_buffer)) / CH2_reverse_increment;
Bits.motor_B_Direction =! motor_B_inverted;
}
fin de calcular la velocidad del motor
} //end de sección de 100 hz
Aquí el si declaración y contador que lo anterior sólo ejecutar a 100Hz han terminado y estamos en la frecuencia de interrupción del contador de tiempo completo. La parte inferior manijas de generar la señal de control del motor de la velocidad calculada anteriormente
pulsos de control al motor
Motor_Phase ++;
if(Motor_Phase > Motor_PWM_Rez) {Motor_Phase = 1;}
Motor A
Si (Motor_A_Speed > = Motor_Phase & & Motor_A_Speed < 20) {}
if(bits.motor_A_Direction == 0) {}
Motor_Pin_A1 = 1;
Motor_Pin_A2 = 0;
}
if(bits.motor_A_Direction == 1) {}
Motor_Pin_A1 = 0;
Motor_Pin_A2 = 1;
}
}
Else {}
Motor_Pin_A1 = 0;
Motor_Pin_A2 = 0;
}
Motor B
Si (Motor_B_Speed > = Motor_Phase & & Motor_B_Speed < 20) {}
if(bits.motor_B_Direction == 0) {}
Motor_Pin_B1 = 1;
Motor_Pin_B2 = 0;
}
if(bits.motor_B_Direction == 1) {}
Motor_Pin_B1 = 0;
Motor_Pin_B2 = 1;
}
}
Else {}
Motor_Pin_B1 = 0;
Motor_Pin_B2 = 0;
}
} //end de interrupción del temporizador
A continuación es el inicio de la interrupción del PCCh. Esta es la parte que se encarga de medir el ancho de pulso. Anteriormente fue creado para ser activado por un flanco ascendente. Cuando detecta el flanco ascendente registrará el tiempo usando CCPR1 cambiará para ver para caer y cambiar la variable PWM1Edge para que coincida con. Cuando detecta caída cambia nuevamente y registra el tiempo.
interrupción de PCCh
if(PIR1bits.CCP1IF == 1)
{
PIR1bits.CCP1IF = 0; claro la bandera
if(PWM1Edge == 1) levantamiento de detección de //if
{
CCP1CON = 0b0000100; //switch para detectar flanco descendente
PWM1Edge = 0; //switch para indicar el borde descendente es el siguiente
PWMGap = CCPR1 - PWM1FallTime; calcular diferencia entre pulso comienza
PWM1RiseTime = CCPR1; //save el valor del temporizador baja para el tiempo de subida
if(PWMGap < 10000) {CH2_width = PWMGap;}
}
else //if detección de caída
{
CCP1CON = 0b0000101; //switch para detectar aumento de borde
PWM1Edge = 1; //switch para indicar el borde de levantamiento es el siguiente
PWM1Width = CCPR1 - PWM1RiseTime; (tiempo de subida del pwm es el momento en que se produjo el ascenso de pwm)
PWM1FallTime = CCPR1; //save el valor del temporizador baja para el tiempo de caída
Realmente necesita entender la lógica detrás de esta parte si necesita modificar el código para trabajar en otros receptores. El receptor traxxas utilicé todos los pulsos pone espalda con espalda. Esto hizo que yo no podía leer a través de un pin porque el conjunto de impulsos era un pulso largo cuando se combina. Por lo que diseñó el programa para que el chip sólo está conectado a cada otra salida, en este caso el servo salidas 1 y 3. De esa manera hay una brecha. La brecha corta (una menos que 10000 según lo detectado por el if declaración abajo) es la intermedia y es la longitud del pulso medio, número de pulso 2. El primer impulso después de la larga distancia es pulso número 1 y el después de la brecha de corto número de pulso 3.
if(PWMGap > 10000) {CH1_width = PWM1Width;}
if(PWMGap < 10000) {CH3_width = PWM1Width;}
}
}
}
No dude en hacer preguntas. El me ayudará a hacer las cosas más claras así que realmente aprecio.
Como ya he mencionado en esa última nota este plan gira en torno a los impulsos que se producen hacia atrás. Algunos receptores les espaciar. Si ese fuera el caso usted no necesita hacer este truco en todos. En cambio usted sólo sabe que después de la larga distancia pulso uno, luego después de cada separación corta adicional estás buscando en el pulso 2, 3, 4 y así sucesivamente. Sólo sería hacer una variable para hacer un seguimiento de cuántos pulsos había capturado desde el boquete pasado y restablecerlo cuando tuvo una larga y, a continuación, utilizar para decidir qué canal atribuye una anchura de pulso capturado a.