Cómo utilizar un codificador rotatorio en un proyecto de microcontrolador. Conexión del codificador al microcontrolador PIC

Este artículo hablará sobre el codificador y cómo conectarlo al microcontrolador. Se puede encontrar en dispositivos como sistemas de audio, lavadoras, hornos microondas y varios dispositivos modernos. Por ejemplo, en los sistemas de audio, se utilizan codificadores y microcontroladores para controlar el volumen. Pero bueno, suficiente agua, pongámonos manos a la obra.

Un codificador, o como también se le llama sensor de ángulo de rotación, es un dispositivo electromecánico que convierte la posición de un ángulo de eje en una señal eléctrica. Los codificadores se dividen en 2 tipos: absolutos e incrementales.

En los codificadores incrementales, cuando se gira el eje, se generan pulsos, el número de estos pulsos es proporcional al ángulo de rotación del eje. Si cuenta estos pulsos, puede averiguar el ángulo de rotación del eje del codificador. Si la perilla del codificador está en reposo, no se generan pulsos. Estos codificadores se utilizan ampliamente en sistemas de audio y controles industriales.

Los encoders absolutos tienen un principio de funcionamiento completamente diferente, basado en emitir un código único para cada posición del eje. La formación de impulsos se produce cuando el eje gira y cuando está en reposo. Además, la información sobre la posición actual del eje se conservará incluso después de interrumpir el suministro de tensión.

En nuestro ejemplo conectaremos un codificador incremental con un microcontrolador. Codificador PEC12 422OF SOO24 que tiene 24 pulsos por revolución.

El codificador tiene 5 pines, 3 de ellos son los pines del propio codificador y los otros dos son el botón. Los pines del codificador tienen un pin común y los otros dos pines de señal. El diagrama de conexión no es diferente del diagrama de conexión de un botón normal. Los pines de señal están conectados a los puertos de E/S del microcontrolador. Y el terminal común, que está conectado a tierra en el medio. Para protegerse contra el rebote de los contactos, puede agregar condensadores con una capacidad de varios nF. Configuramos los pines a los que conectamos el codificador en el programa como entradas y activamos las resistencias pull-up, puedes conectar resistencias externas.

Diagrama de conexión del codificador al microcontrolador.

El principio de funcionamiento del codificador se basa en el cierre y apertura de contactos cuando nadie gira el mando, hay uno lógico en la entrada del MK. Cuando comienza a girar la perilla, aparecen dos pulsos rectangulares que se desplazan entre sí. La dirección en la que giremos determinará qué señal está más adelante.

Pero como hay varios contactos en uno, la imagen se verá así.

Algoritmo del programa del microcontrolador.

En un cierto intervalo, comienza la llamada a la función de sondeo del codificador. Esta función lee los niveles lógicos que están presentes en los pines del microcontrolador y escribe este valor en una variable temporal. Dentro de la función de sondeo del codificador, hay otra variable estática que se guarda al salir de esta función y almacena la secuencia de valores anteriores; De esta variable se toma el último valor registrado y se compara con el actual para determinar si ha habido cambios. Si estos valores son iguales, la función sale, y si difieren, entonces el valor de la variable estática se desplaza 2 bits hacia la izquierda y se escribe un nuevo valor (actual) en el espacio "libre".

Resulta que cuando el eje del codificador gira, se escribirá constantemente un nuevo valor en la variable temporal y se obtendrá una secuencia de código repetida. Si lo giramos hacia la derecha: 11100001, y si lo giramos hacia la izquierda, entonces 11010010. Usando estos valores, puedes entender en qué dirección gira el eje.

El archivo contiene 2 archivos encoder.h y encoder.c. Inicialmente, debe configurar el puerto y la cantidad de pines a los que se realiza la conexión. Estas son las variables LEFT_SPIN y RIGHT_SPIN. El archivo "c" contiene la implementación de las funciones.

En uno de los artículos ya hablé del principio de funcionamiento, el diagrama de conexión y el código fuente de la biblioteca para trabajar con un codificador incremental. Hoy hablaremos de la aplicación práctica del codificador. Como ejemplo, elegí un programa generador de ondas cuadradas con un rango de frecuencia operativa de 1 a 100 Hz. El plan original suponía un rango de 1 a 1000 Hz, pero en la práctica resultó que pasar por mil valores es tedioso incluso con un codificador.

Preparación

Crear un nuevo proyecto en un espacio de trabajo vacío

Proyecto > Crear nuevo proyecto…

Tipo de plantilla C > principal

Copie los archivos fuente de la biblioteca para trabajar con el codificador a la carpeta del proyecto
codificador.h y codificador.c

Conectamos el archivo encoder.c a nuestro proyecto
Botón derecho del mouse en la ventana del espacio de trabajo y en el menú que se abre Agregar > Agregar archivos...

Copie el archivo bits_macros.h a la carpeta del proyecto.


Incluyendo archivos de encabezado

Al principio del archivo main.c escribimos las siguientes líneas
#incluir
#incluir
#incluir "codificador.h"
#incluir "bits_macros.h"

Establecer la configuración del proyecto

Proyecto > Opciones

Tipo de microcontrolador
Opciones generales > Destino > Configuración del procesador > ATMega8535

Permitir el uso de nombres de bits definidos en archivos de encabezado
Opciones generales > Sistema > Habilitar defensas de bits...

Optimización del código para el tamaño
Compilador C/C++ > Optimizaciones > Tamaño alto

Tipo de archivo de salida
Vinculador > Archivo de salida: marque Anular el valor predeterminado y cambie la extensión a hexadecimal
Vinculador > Formato > Otro seleccione Estándar Intel

Haga clic en Aceptar. Guarde el proyecto y el espacio de trabajo.
Ahora tenemos un proyecto vacío con una biblioteca conectada y configuraciones especificadas.

Tarea

Haga que el microcontrolador genere una onda cuadrada con una frecuencia de 1 a 100 Hz. El valor de la frecuencia debe configurarse mediante un codificador. Girar el codificador una posición debería corresponder a un cambio en la frecuencia del generador de 1 Hz.

Diagrama para nuestro ejemplo.

Se conecta un LED al pin en el que se generará el meandro para poder ver al menos de alguna manera el resultado del programa. Es poco probable que mucha gente tenga un osciloscopio a mano.

Algoritmo del programa

La señal de onda cuadrada se genera utilizando un temporizador T1 de 16 bits, que funciona en el modo CTC y se reinicia cuando coincide. La memoria flash del microcontrolador almacena una matriz que contiene una constante para cada valor de la frecuencia requerida. La variable pTimerValue se utiliza para acceder a los elementos de la matriz. En las interrupciones del temporizador T1, el valor de la constante se lee y se escribe en el registro de comparación.

El pin PD5 (OC1A) se utiliza para generar la señal. Tiene funciones alternativas: puede cambiar su estado al opuesto si el registro de conteo y el registro de comparación son iguales.

En el programa principal, en un bucle while sin fin, el microcontrolador sondea el búfer del codificador y, dependiendo de su valor, disminuye o aumenta la variable pTimerValue.

Al comienzo de main hay un código para inicializar los periféricos y las variables necesarias.

Estructura del programa

Para mayor claridad, he representado la estructura del programa en forma de diagrama.

Esta es una estructura típica para construir programas simples. Las interrupciones ocurren naturalmente en puntos aleatorios del ciclo.

  • Inicialización.
  • Un bucle infinito (el llamado superbucle) en el que se espera un evento, generalmente en forma de indicadores de sondeo o algún tipo de búfer.
  • Funcionamiento paralelo de dispositivos periféricos que provocan interrupciones. Ejecutan algún código (preferiblemente corto) y establecen indicadores.

Para tareas sencillas, este enfoque es suficiente. Para los complejos, existen otras formas de organizar programas. Ten paciencia, las cosas les llegarán pronto.

Cálculo de constantes para el temporizador T1.

Calculemos el valor de la constante para una frecuencia de 1 Hz. Ya he dado un cálculo similar, pero sería útil recordarlo.

La frecuencia de reloj del microcontrolador es de 16 MHz (ver diagrama). El factor del preescalador del temporizador es 256. Le permite obtener interrupciones con cualquier frecuencia de nuestro rango.

El período de un tic del temporizador será igual a 1/(16 MHz/ 256) = 16 µs

En el pin PD5 necesitamos recibir una señal con una frecuencia de 1 Hz. El pin cambia su estado para cada interrupción del temporizador, lo que significa que la frecuencia de interrupción debe ser 2 veces mayor. Para nuestro caso - 2 Hz.

¿Cuántos tics del temporizador caben en 2 hercios? (1/2 Hz)/16 µs = 31250
Esta es la constante deseada.

Los valores restantes se calculan de la misma forma. Normalmente uso Excel para esto.


Colocamos los valores resultantes en un array.

__destello entero sin firmar valor del temporizador =
{

guárdelo en un archivo separado: timer_value.h y conéctelo al archivo main.c

#incluir "valor_temporizador.h"

Sí, aún necesitas agregar un par de constantes a este archivo.

#definir MAX_TIM_VALUE 99
#definir MIN_TIM_VALUE 0

Asegurémonos de haber calculado correctamente las constantes del temporizador. Lancemoslo. El código del programa será así.

//Programación AVR en C

//sitio web 17/10/09
#incluir
#incluir
#incluir "codificador.h"
#incluir "bits_macros.h"
#incluir "valor_temporizador.h"

//índice para acceder a los elementos de la matriz
carácter volátil sin firmar pTimerValue = 0;

entero principal( vacío )
{
//inicializa el temporizador T1
TCNT1 = 0;
TCCR1A = (0<TCCR1B = (0<

//configura el pin PD5 en salida
EstablecerBit(PORTD, PD5);
EstablecerBit(DDRD, PD5);

//no hacer nada en un bucle sin fin
mientras(1);
devolver 0;
}

Creo que sólo la parte de inicialización del temporizador requiere explicación.

Restablecer el registro del contador
TCNT1 = 0;

Inicialización de registros de configuración del temporizador T1.
TCCR1A = (0<TCCR1B = (0<

Donde los bits WGM13, WGM12, WGM11, WGM10 configuran el modo de funcionamiento del temporizador - CTC,
CS12, CS11, CS10: determina el coeficiente del preescalador del temporizador –256,

COM1A1, COM1A0 – determinan el comportamiento del pin PD5(OC1F) – en este caso, basándose en una señal del temporizador, cambiará su estado al opuesto


Inicialice el registro de coincidencia al valor inicial.
OCR1A = valor del temporizador;

Compilamos el programa y lo cargamos en el microcontrolador. El LED debe parpadear a una frecuencia de 1 Hz.
No hay interrupciones en el programa. No hay manipulación del pin PD5. ¡Sin embargo, el LED parpadea!

Programa

Ahora necesitas "atornillar" el codificador a este programa. Configuremos la configuración en el archivo de encabezado encoder.h: el puerto y los pines a los que está conectado el codificador, los valores de las constantes.


#define PORT_Enc PORTA
#definir PIN_Enc PINA
#definir DDR_Enc DDRA
#definir Pin1_Enc 2
#definir Pin2_Enc 1

#definir RIGHT_SPIN 0x01
#definir LEFT_SPIN 0xff

El encabezado contiene prototipos de tres funciones. Recordemos su propósito.

anular ENC_InitEncoder (anular) Configura los pines del microcontrolador a los que está conectado el codificador a la entrada. Esta función debe llamarse al comienzo de main.


anular ENC_PollEncoder (anular)– sondea el codificador una vez, analiza los estados actual y anterior y escribe las constantes correspondientes (RIGHT_SPIN y LEFT_SPIN) en el búfer. Esta función se ubicará en la interrupción del temporizador T0.


carácter sin firmar ENC_GetStateEncoder (vacío)– devuelve el contenido del búfer del codificador. Si la rotación en una posición no fue fija, la función devolverá 0; si la rotación fue fija, la función devolverá el valor de la constante correspondiente. Esto borrará el valor del búfer. Esta función se llamará en el programa principal, en el ciclo while.


Estamos ampliando nuestro programa. Puedes intentar hacerlo tú mismo.

//Programación AVR en C
//un ejemplo de uso de un codificador
//sitio web 17/10/09

#incluir
#incluir
#incluir "codificador.h"
#incluir "bits_macros.h"
#incluir "valor_temporizador.h"

#definir TCNT0_const 253
#definir TCCR0_const 5

carácter volátil sin firmar pTimerValue = 0;

entero principal( vacío )
{
ENC_InitEncoder();

//inicializa el temporizador t0
TCNT0 = TCNT0_const;
TCCR0 = TCCR0_const;

//inicializa el temporizador t1
TCNT1 = 0;
TCCR1A = (0<TCCR1B = (0<OCR1A = valor del temporizador;

//habilitar interrupciones del temporizador
//t0 - por desbordamiento, t1 - por coincidencia

TIMSK = (1<

//configura PD5 en salida
EstablecerBit(PORTD, PD5);
EstablecerBit(DDRD, PD5);

__enable_interrupt ();
mientras (1){
//leer el contenido del buffer del codificador
//después de leer se borra

carácter sin firmar stateEnc = ENC_GetStateEncoder();

//si no está vacío
si(estadoEnc! = 0)(
//determinar la dirección de rotación y cambiar la variable timerValue
si(estadoEnc == RIGHT_SPIN)(
si(pTimerValue == MAX_TIM_VALUE) pTimerValue = MIN_TIM_VALUE;
demás pTimerValue++;
}
si(estadoEnc == LEFT_SPIN) (
si(pTimerValue == MIN_TIM_VALUE) pTimerValue = MAX_TIM_VALUE;
demás pTimerValue--;
}
}
}
devolver 0;
}

//encuesta del codificador
#pragma vector=TIMER0_OVF_vect
__interrumpirvacío temporizador0_ovf_my( vacío)
{
TCNT0 = TCNT0_const;
ENC_PollEncoder();
}

#pragma vector=TIMER1_COMPA_vect
__interrumpir vacío timer1_compa_my( vacío)
{
//actualiza el valor del registro de comparación
OCR1A = valor del temporizador;
}

Parece que todo debería quedar claro.
El fragmento de código que cambia el valor de pTimerValue también podría escribirse así:

si(estadoEnc != 0) (
pTimerValue = pTimerValue + estadoEnc;
si(pTimerValue == (MAX_TIM_VALUE + 1)) pTimerValue = MIN_TIM_VALUE;
más si(pTimerValue == (MIN_TIM_VALUE - 1)) pTimerValue = MAX_TIM_VALUE;
}

Cuando el codificador se gira hacia la derecha, pTimerValue se suma a 1, es decir, se incrementa.

Al girar el codificador hacia la izquierda, pTimerValue se suma a 0xff, lo que equivale a restar 1. Es la misma operación, pero el resultado es exactamente el contrario.

Aprenda a utilizar un codificador rotatorio incremental en un proyecto Arduino.

Un codificador rotatorio es un dispositivo electromecánico que convierte el movimiento de rotación en información digital o analógica. Es muy similar a un potenciómetro, pero se puede girar indefinidamente en el sentido de las agujas del reloj o en el sentido contrario a las agujas del reloj. Hay varios tipos de codificadores rotatorios. Los dos tipos principales son codificadores absolutos y relativos (incrementales). Mientras que un codificador absoluto genera un valor proporcional al ángulo actual del eje, un codificador incremental genera el paso y la dirección del eje. Los codificadores rotatorios son cada vez más populares en la electrónica de consumo, especialmente como botones de control, además de aplicaciones en muchos otros campos. Reemplazan potenciómetros y botones de navegación donde se requiere navegación, configuración, entrada de datos y selección de menú rápidas. Algunos codificadores también incluyen un botón incorporado que crea una entrada adicional al procesador que puede usarse como otro comando de usuario en la interfaz de control. En la imagen siguiente puede ver un codificador rotatorio incremental típico con un botón de encendido.

En este artículo, le mostraremos cómo utilizar un codificador rotatorio incremental en un proyecto Arduino. Explicaremos cómo lidiar con el rebote de contactos e interpretar las señales del codificador en un programa de microcontrolador mediante interrupciones.

Señal de salida de cuadratura del codificador incremental

Un codificador rotatorio incremental produce dos señales de salida a medida que gira el eje, también llamada salida en cuadratura. Dependiendo de la dirección, una señal va delante de la otra. A continuación puede ver la forma de onda de salida de un codificador rotatorio incremental y la secuencia de bits esperada.

Como puede verse en la figura, ambas salidas se encuentran inicialmente en el estado lógico. Cuando el eje del codificador comienza a girar en el sentido de las agujas del reloj, el estado en la salida A cae primero a cero lógico, y luego la salida B sigue con un retraso. Cuando gira en el sentido contrario a las agujas del reloj, sucede lo contrario. Los intervalos de tiempo en el diagrama de señales dependen de la velocidad de rotación, pero el retraso de la señal está garantizado en cualquier caso. Basándonos en esta característica de un codificador rotatorio incremental, escribiremos un programa para Arduino.

Filtrado del rebote de contactos de un codificador mecánico

Los codificadores mecánicos tienen interruptores incorporados que generan una señal de salida en cuadratura durante la rotación.

Cuando se trata de señales de codificador, el principal problema es el rebote de los contactos. Esto provoca una determinación errónea del sentido de rotación y de la cantidad de rotación del eje del codificador y hace que el uso de codificadores sea problemático. Podemos deshacernos del rebote de contactos filtrándolo en un programa o usando esquemas de filtrado adicionales.

El filtrado de ruido en el software del microcontrolador es una opción de filtrado, pero tiene algunas desventajas. Necesita escribir código más complejo para manejar el ruido. El filtrado consumirá tiempo de procesamiento e introducirá retrasos en el hilo principal del programa. Es posible que tengas que configurar temporizadores para ignorar los intervalos de rebote de contactos. Al final, es posible que no consigas obtener un resultado satisfactorio y fiable.

El filtrado de ruido con hardware adicional es más sencillo y detiene el ruido en su origen. Necesitará un filtro RC de primer orden. En la siguiente imagen puedes ver cómo se ve la señal después de usar un filtro RC.

El filtro RC ralentiza el tiempo de caída y de subida y proporciona protección contra rebotes del hardware. Al seleccionar un par resistencia-condensador, se debe considerar la velocidad máxima. De lo contrario, se filtrará la respuesta esperada del codificador.

Aplicación sencilla

Crearemos una aplicación que demuestre cómo usar un codificador rotatorio en un proyecto Arduino. Utilizaremos el codificador para navegación, entrada de datos y selección. A continuación se muestra un diagrama esquemático de la aplicación.

El circuito está basado en la placa Arduino Uno. La pantalla LCD del Nokia 5110 se utiliza para la interfaz gráfica. Se agrega un codificador giratorio mecánico con un botón y filtros RC como controles.

Desarrollaremos un menú de software simple en el que demostraremos el funcionamiento de un codificador rotatorio.

Procesamiento de señales de codificador mediante interrupciones

Las señales del codificador deben detectarse e interpretarse en el programa lo más rápido posible para no bloquear el flujo principal del programa. Podemos detectar señales sondeando en el bucle principal o utilizando interrupciones. El sondeo no es eficiente porque necesita reservar tiempo y recursos en el bucle principal, lo que introduce retrasos adicionales. El uso de interrupciones es una solución más rápida y rentable. Le mostraremos cómo utilizar interrupciones para procesar señales del codificador.

El Atmega328 tiene dos tipos de interrupciones que pueden usarse para este propósito; interrupción externa e interrupción de cambio de salida. Los pines INT0 e INT1 se asignan a una interrupción externa, y PCINT0 - PCIN15 se asignan a una interrupción cuando un pin cambia de estado. Una interrupción externa puede detectar si la señal de entrada está bajando o subiendo y puede activarse bajo una de las siguientes condiciones: subida, bajada o conmutación. Hay muchos más recursos de hardware disponibles para la interrupción de cambio de pin, pero no puede detectar los flancos ascendentes y descendentes, y se llama cuando se produce cualquier cambio de estado lógico (alternancia) en un pin.

Para usar la interrupción de cambio de pin, conecte las salidas de giro del codificador A y B a los pines A1 y A2, y la salida del botón al pin A0 de la placa Arduino, como se muestra en el diagrama del circuito. Configure los pines A0, A1 y A2 en modo de entrada y encienda sus resistencias pull-up internas. Habilite la interrupción de cambio de pin en el registro PCICR y habilite las interrupciones para los pines A0, A1 y A2 en el registro PCMS1. Si se detecta algún cambio en el estado lógico en una de estas entradas, se llamará ISR(PCINT1_vect) (Pin State Interrupt).

Dado que la interrupción de cambio de pin se solicita para cualquier cambio lógico, necesitamos monitorear ambas señales (tanto A como B) y detectar la rotación cuando se recibe la secuencia esperada. Como se puede ver en el diagrama de señales, el movimiento en el sentido de las agujas del reloj genera A = …0011… y B = …1001…. Cuando escribimos ambas señales en los bytes seqA y seqB, desplazando la última lectura hacia la derecha, podemos comparar estos valores y determinar el nuevo paso de rotación.

Puede ver parte del código que incluye la función de interrupción de inicialización y cambio de pin.

Configuración nula() ( pinMode(A0, INPUT); pinMode(A1, INPUT); pinMode(A2, INPUT); // Habilita las resistencias pull-up internas digitalWrite(A0, HIGH); digitalWrite(A1, HIGH); digitalWrite( A2, HIGH); PCICR = 0b00000010; // 1. PCIE1: Habilitar interrupción de cambio de estado 1 PCMSK1 = 0b00000111 // Habilitar interrupción de cambio de estado para A0, A1, A2) void loop() ( // Bucle principal) ISR (PCINT1_vect) ) ( // Si la interrupción es causada por un botón if (!digitalRead(A0)) ( botón = true; ) // Si la interrupción es causada por señales del codificador else ( // Lee las señales A y B booleanas A_val = digitalRead( A1); boolean B_val = digitalRead (A2); // Escribe las señales A y B en secuencias separadas seqA);<<= 1; seqA |= A_val; seqB <<= 1; seqB |= B_val; // Маскировать четыре старших бита seqA &= 0b00001111; seqB &= 0b00001111; // Сравнить запсанную последовательность с ожидаемой последовательностью if (seqA == 0b00001001 && seqB == 0b00000011) { cnt1++; left = true; } if (seqA == 0b00000011 && seqB == 0b00001001) { cnt2++; right = true; } } }

El uso de una interrupción externa simplifica el proceso, pero como esta interrupción solo tiene dos pines asignados, no podrás usarla para otros fines si la ocupas con un codificador. Para usar una interrupción externa, debe configurar los pines 2 (INT0) y 3 (INT1) en modo de entrada y encender sus resistencias pull-up internas. Luego seleccione la opción de flanco descendente para activar ambas interrupciones en el registro EICRA. Habilite las interrupciones externas en el registro EIMSK. Cuando el eje del codificador comienza a girar, primero la señal principal cae al cero lógico y la segunda señal permanece en el uno lógico durante algún tiempo. Por lo tanto, necesitamos determinar cuál de las señales está en el estado lógico durante la interrupción. Después de que la señal principal haya caído al cero lógico, después de algún tiempo la segunda señal también caerá al cero lógico, provocando otra interrupción. Pero esta vez y la otra señal (principal) estarán en un nivel lógico bajo, lo que significa que no es el inicio de la rotación, por lo que la ignoramos.

A continuación puede ver parte del código que incluye la función de inicialización y manejo de interrupciones externas.

Void setup() ( pinMode(2, INPUT); pinMode(3, INPUT); // Habilitar resistencias pull-up internas digitalWrite(2, HIGH); digitalWrite(3, HIGH); EICRA = 0b00001010; // Seleccionar llamada en flanco descendente EIMSK = 0b00000011; // Habilitar interrupción externa ) void loop() ( // Bucle principal ) ISR (INT0_vect) ( // Si la segunda señal está en el estado lógico, entonces esta es una nueva rotación if (digitalRead( 3) == ALTO) ( izquierda = verdadero; ) ) ISR (INT1_vect) ( // Si la segunda señal está en el estado lógico, entonces esta es una nueva rotación if (digitalRead(2) == ALTO) ( derecha = verdadero; ) )

El código completo para el boceto de Arduino, incluido el bucle principal, se encuentra a continuación:

#incluir #incluir #incluir byte volátil seqA = 0; byte volátil seqB = 0; byte volátil cnt1 = 0; byte volátil cnt2 = 0; booleano volátil derecho = falso; booleano volátil izquierda = falso; botón booleano volátil = falso; retroiluminación booleana = verdadero; elemento de menú de bytes = 1; página de bytes = 1; Pantalla Adafruit_PCD8544 = Adafruit_PCD8544(13, 12,11, 8, 10); void setup() ( pinMode(A0, INPUT); pinMode(A1, INPUT); pinMode(A2, INPUT); // Habilita las resistencias pull-up internas digitalWrite(A0, HIGH); digitalWrite(A1, HIGH); digitalWrite( A2, ALTA); // Habilitar retroiluminación LCD pinMode(9, OUTPUT); digitalWrite(9, ALTA); // 1. PCIE1: Habilitar interrupción de cambio de estado 1 = 0b00000111; (2); // Establece la orientación LDC display.begin(60); // Establece el contraste de la pantalla LCD display.clearDisplay(); // Borra la pantalla display.display()/Aplica cambios sei(); () ( // Crear páginas de menú if (page==1) ( display.setTextSize(1); display.clearDisplay(); display.setTextColor(NEGRO, BLANCO); display.setCursor(15, 0); display.print ("MENÚ PRINCIPAL"); display.drawFastHLine(0,10,83,BLACK); display.setCursor(0, 15); if (menuitem==1) ( display.setTextColor (BLANCO, NEGRO); else ( display. setTextColor(NEGRO, BLANCO);<<= 1; seqA |= A_val; seqB <<= 1; seqB |= B_val; // Маскировать четыре старших бита seqA &= 0b00001111; seqB &= 0b00001111; // Сравнить запсанную последовательность с ожидаемой последовательностью if (seqA == 0b00001001 && seqB == 0b00000011) { cnt1++; left = true; } if (seqA == 0b00000011 && seqB == 0b00001001) { cnt2++; right = true; } } }

Puedes ver el codificador en acción en el vídeo a continuación.

Hace tiempo que quería adaptar a mi portátil un control de volumen hecho de codificador. Necesitará conectar este regulador a un USB para que todo esté "adulto" (y no haya otra forma de conectar un dispositivo externo a la computadora portátil). Gire el codificador hacia la izquierda (el volumen debería disminuir, hacia la derecha) debería aumentar. Presionamos la perilla del codificador hacia abajo: iniciamos algún programa útil o cambiamos al control de tono.

Para aquellos que no saben qué es un codificador: es una perilla, como una perilla de volumen basada en una resistencia normal, solo que esta perilla no tiene posiciones límite: gírela tanto como desee en cualquier dirección. El codificador gira con agradables clics suaves, pero parece una resistencia variable normal.

Estos dispositivos no son infrecuentes en las radios de automóviles modernas y en cualquier dispositivo doméstico cuya interfaz de usuario sea procesada por un microcontrolador (y esto es casi cualquier electrodoméstico) y donde se necesita un ajuste o sintonización suave. A menudo se incorpora un tercer contacto en el codificador, que funciona como un botón en un mango: cuando presionamos el mango del codificador hacia abajo (a lo largo del eje), este botón se activa. Enriquece enormemente las posibilidades de la interfaz de usuario: en un codificador se puede construir todo el sistema de control de un dispositivo electrónico (pero esto agrega dolores de cabeza al programador, pero son cosas menores). Acabo de tener un codificador de este tipo.

El principio de funcionamiento del codificador es bastante simple: tiene solo dos contactos (el botón en el mango no cuenta), que comienzan a cerrarse tan pronto como el usuario comienza a girar el mango del codificador. Los contactos están conectados a dos patas del microcontrolador (que funcionan como entradas digitales), y cuando se gira la perilla del codificador, aparecen pulsos en estas patas, mediante cuya fase y cantidad el microcontrolador determina la dirección de rotación y el ángulo de rotación. del mando codificador.

Para que el control de volumen funcione, es necesario resolver al menos tres problemas de ingeniería:

Paso 1. Creación de un dispositivo USB de baja velocidad en una placa de pruebas.
Paso 2. Conecte un codificador a este dispositivo USB, asegúrese de que el microcontrolador lo procese y transmita información sobre la rotación del codificador a la computadora.
Paso 3. Comprenda cómo puede controlar mediante programación el control de volumen. Seguramente existe algún tipo de API multimedia que te permite hacer esto. Programa mínimo: debe escribir un programa que reciba señales de un dispositivo USB y controle el volumen. Sería bueno, por supuesto, escribir un conductor, pero da un poco de miedo emprenderlo. Mejor dejarlo para más tarde.

Entonces, describiré el proceso de creación de un regulador paso a paso. Omitiré los detalles, de lo contrario será demasiado aburrido. Para aquellos interesados, consulte las fuentes y la documentación en los enlaces.

[Paso 1: cree un dispositivo USB de baja velocidad en una placa]

Este paso se realizó sin siquiera comenzar, de alguna manera demasiado simple y banal. Estúpidamente descargué un proyecto de ejemplo del enlace. Corregí el archivo usbconfig.h: le puse un nombre a mi dispositivo para mostrarlo DEMOSTRACIÓN DEL CODIFICADOR, no había suficiente imaginación para más. Verifiqué el tipo de procesador (ATmega16) y la frecuencia de cuarzo (16 MHz) en el Makefile para que coincidan con mi placa de pruebas AVR-USB-MEGA16. Compilé el proyecto en AVRStudio, actualicé la placa, la conecté a la computadora; todo comenzó media vuelta, mi dispositivo USB funcionó correctamente como un puerto COM virtual; todo es exactamente como está escrito en el artículo.

[Paso 2. Conecte el codificador al dispositivo USB]

Este paso me provocó los mayores temores de que todo funcionara como debería. Que conectaría el codificador y podría leerlo, no tenía ninguna duda al respecto. Había dudas de que pudiera leerlo de manera eficiente cuando el procesamiento del protocolo USB también se ejecutaba en segundo plano; después de todo, esta no es una tarea fácil para un microcontrolador (como resultó más tarde, era completamente en vano preocuparme). ).

Como de costumbre, comencé a buscar en Internet subrutinas preparadas para leer el codificador. Rápidamente encontré lo que necesitaba, específicamente para AVR, código muy simple en C, archivos encoder.c y encoder.h. Digas lo que digas, el código abierto es algo genial.

Adjunté dos indicadores LED, VERDE y AMARILLO, para indicar la dirección de rotación del codificador. Para mayor comodidad, conecté el codificador directamente al conector ISP, aprovechando que las señales MOSI, MISO y SCK son solo los pines PB5, PB6 y PB7 del microcontrolador ATmega16 (allí conecté las fases A y B, así como un botón codificador).

Se corrigieron las definiciones de las piernas y se agregó el código de inicialización. Agregué el módulo encoder.c al proyecto. Se agregó control de LED verdes y amarillos al bucle principal cuando la información proviene del codificador. El LED ROJO está vinculado al botón del codificador: cuando lo presionamos, el LED rojo se enciende, cuando lo soltamos, se apaga. Lo compilé, lo actualicé y funciona. Giro la perilla hacia la izquierda y el LED verde parpadea al mismo tiempo que los clics del codificador. Giro la perilla hacia la derecha y el LED amarillo parpadea. A pesar de que el codificador se lee mediante el método de sondeo, gracias al código eficiente, NO hay quejas sobre la lectura del codificador, incluso cuando se trabaja simultáneamente con la biblioteca V-USB (¡respeto, Pashgan!). Se agregó salida de información desde el codificador al puerto COM virtual (gire el codificador hacia la izquierda y muestre los signos menos "-" en la consola; gírelo hacia la derecha para mostrar los signos más "+" en la consola). Usando un temporizador, cada 10 ms muestro el estado del botón del codificador y lo indico con un LED rojo (se presiona el botón - transmito el símbolo "1", cuando lo suelto - "0"). Todo funciona. Aburrido.

Finalmente, descarté los módulos cmd.c, crc16.c, eepromutil.c, strval.c. El tamaño del código se ha reducido a 3 kilobytes; genial, ahora cabe en la memoria ATtiny45 (puedes usar la placa de pruebas AVR-USB-TINY45 en el futuro, es más pequeña y más barata).

[Paso 3. Descubra cómo controlar mediante programación el control de volumen]

Como de costumbre, busqué en Google la pregunta. Saqué un montón de basura y finalmente recogí una perla. Entonces es una cuestión de tecnología. Saco mi kit de construcción infantil favorito: Visual Studio. Sin pensar en nada, generé una aplicación basada en diálogos usando el asistente. Coloco el control deslizante de volumen en el panel, le vinculo una variable y agrego un controlador para la posición del control deslizante. Cuando se inicia la aplicación, configuro el motor a un mínimo de 0 y un máximo de 65535 (para corresponder a los límites del valor de volumen manipulado por las bibliotecas de control del mezclador). lo leo con una funcion mezcladorGetControlDetails valor de volumen actual y ajuste el control deslizante en la posición adecuada. En el controlador de posición del control deslizante, todo es al revés: leo la posición del control deslizante y uso la función mezcladorEstablecerControlDetalles Configuré el volumen deseado. Controlo el volumen exactamente como está escrito en el artículo. Lo comprobé: funciona.

Ahora sólo queda leer lo que sale del puerto COM virtual (tenemos un dispositivo USB recién horneado con un codificador colgado). Si recibe un signo menos (-), mueva el control deslizante hacia la izquierda (reduzca el volumen), un signo más (+) y luego mueva el control deslizante hacia la derecha (aumente el volumen). Si llegan los caracteres 0 y 1, entonces controlamos el estado de la casilla de verificación en consecuencia (solo para indicar si el botón del codificador está presionado o no). Puede trabajar con un puerto COM como con un archivo normal (ver). Inicializamos la conexión al puerto COM abriendo un archivo (llamando ::Crear archivo) en modo de bloqueo. Lanzamos un hilo separado, agregamos lectura de archivos al bucle infinito (con una llamada de bloqueo ::Leer archivo) un personaje a la vez y analiza este personaje. Según el símbolo que ha llegado, giramos el control deslizante en la dirección deseada (el controlador del control deslizante ajustará el volumen) o actualizamos el estado de la casilla de verificación. Lo comprobé: funciona.

Eso es todo, en realidad. Entonces podrás realizar infinitas (y probablemente inútiles) mejoras. Realice una búsqueda automática del puerto COM virtual requerido (ahora, para simplificar las cosas, el nombre del puerto COM se pasa a través de la línea de comando). Convertir un dispositivo USB con Centros para el Control y la Prevención de Enfermedades-clase en escondido- esto puede simplificar el código de un dispositivo USB, así como simplificar la búsqueda programática y la apertura del dispositivo en la computadora mediante VID y HID. O escriba un servicio en lugar de un programa (para no tener que ejecutar un programa por separado). O incluso un conductor. Esto es muy interesante, pero no sé cómo (¿tal vez uno de los Khabravoitas pueda enseñarme la sabiduría?...). Adjunte alguna acción al botón del codificador. Y así hasta el infinito.

Espero que alguien encuentre útil mi investigación en sus propios desarrollos. Si me perdí algo, estaré encantado de escuchar sus comentarios en los comentarios.

[UPD120803]

Una persona competente montó un AVR en un microcontrolador

Me ordenaron desarrollar un programa para un dispositivo que utiliza un codificador incremental como elemento de control. Por lo tanto, decidí escribir una lección no programada sobre cómo trabajar con un codificador en el sistema Arduino.

Muy brevemente de qué estamos hablando, es decir. sobre la clasificación de codificadores.

Los codificadores son sensores digitales de ángulo de rotación. En otras palabras, convertidores de código angular.

Recalco que se trata de sensores digitales porque existen una gran cantidad de sensores con señales de salida analógicas. Una resistencia variable simple puede considerarse un sensor de ángulo. Los codificadores generan señales discretas en la salida.

Los codificadores pueden ser absolutos o acumulativos (incrementales).

Los codificadores absolutos generan un código de salida correspondiente al ángulo actual de la posición del eje. No tienen memoria. Puede apagar el dispositivo, girar el eje del codificador, encenderlo y la salida será un nuevo código que muestra la nueva posición del eje. Dichos codificadores son complejos, costosos y, a menudo, utilizan interfaces digitales estándar RS-485 y similares para la conexión.

Los codificadores incrementales generan pulsos de salida que aparecen cuando se gira el eje. El dispositivo receptor puede determinar el ángulo actual del eje del codificador contando el número de pulsos en su salida. Los codificadores incrementales no pueden detectar la posición del eje después del encendido. Es necesario vincularlo al inicio de la cuenta regresiva.

Pero en la mayoría de los casos no es necesario conocer el valor absoluto del ángulo actual. Si usamos un codificador, por ejemplo, para ajustar el nivel de volumen, entonces necesitamos aumentarlo en varias gradaciones o disminuirlo. No miramos la perilla del codificador; no tiene escala. Necesitamos determinar el cambio de ángulo con respecto a la posición actual. Lo mismo ocurre con la configuración de parámetros en la pantalla. Giramos la perilla del codificador y observamos cómo cambia el valor del parámetro en la pantalla.

En tales casos, los codificadores incrementales se convierten en dispositivos ideales de control, configuración de parámetros y selección de menús. Son mucho más convenientes que los botones "+" y "-".

En estos casos, se pueden utilizar los codificadores incrementales mecánicos más sencillos y de bajo precio.




Arriba