Tengo ATMEGA328P-PU y le he conectado la fotocélula. Estoy usando el siguiente código para leer los valores analógicos y enviarlos a la computadora para su depuración.
#define F_CPU 800000UL
#define BAUD 11400
#define BRC ((F_CPU/8/BAUD) - 1)
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <util/setbaud.h>
#include <avr/io.h>
#include <avr/interrupt.h>
double dutyCycle = 0;
int main(void)
{
initSerial();
DDRD = (1 << PORTD6);
TCCR0A = (1 << COM0A1) | (1 << WGM00) | (1 << WGM01);
TIMSK0 = (1 << TOIE0);
setupADC();
sei();
TCCR0B = (1 << CS00) | (1 << CS02);
while(1)
{
unsigned int value = ADCW;
UDR0 = (value*100/1024) ;
_delay_ms(1);
}
}
void initSerial()
{
UBRR0H = (BRC >> 8);
UBRR0L = BRC;
UCSR0B = ( 1 << TXEN0 );
UCSR0C = ( 1 << UCSZ00 ) | ( 1 << UCSZ01 );
}
void setupADC()
{
ADMUX = (1 << REFS0) | (1 << MUX0) | (1 << MUX2);
ADCSRA = (1 << ADEN) | (1 << ADIE) | (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
DIDR0 = (1 << ADC5D);
startConversion();
}
void startConversion()
{
ADCSRA |= (1 << ADSC);
}
ISR(TIMER0_OVF_vect)
{
OCR0A = dutyCycle;
}
ISR(ADC_vect)
{
dutyCycle = ADC;
startConversion();
}
Yo uso el siguiente comando para ver la salida del sensor:
od -d /dev/ttyACM3 -w2
Puedo ver el cambio de salida en función de la cantidad de luz, pero cambia realmente extraño, si elimino lentamente la fuente de luz del sensor obtengo esta salida:
3542
3298
3142
2799
2243
2142
1799
1543
1542
1537
1542
262
257
1542
262
257
1542
257
1537
1542
1
256
257
262
65535
255
0
65535
0
65278
65535
65278
30969
30968
63736
63993
63737
63479
63992
La primera mitad se ve muy bien, pero de repente salta a 63k, esto sucede cada 10 cm, luego baja de 63k a 0 y luego vuelve a saltar a 63k. ¿Alguien puede decirme cómo convertir ADCW a valor decimal? Estoy esperando un número entre 0 y 1024 como salida.
El manual dice "ADC tiene una resolución de 10 bits, requiere 10 bits para almacenar el resultado", ¿eso no significa que es de 10 bits (16 bits) en lugar de 8 bits?
No puede empujar un valor de varios bytes a través de un canal serie de 8 bits en una sola operación. Como no está seleccionando varios campos de bits en el valor de origen, lo que realmente sucede es que está enviando los 8 bits menos significativos de lecturas sucesivas. Y luego malinterpretar 2 o 4 de ellos seguidos como valores falsos de varios bytes.
Si puede tolerar 256 valores distintos , puede modificar su código para cambiar a la derecha el valor de conversión (u operar en modo de 8 bits para comenzar) para que envíe una medida legítima de un solo byte sin firmar. Luego, deberá asegurarse de que el dispositivo serie de Linux esté funcionando en un modo completamente sin formato, de modo que no se den interpretaciones especiales a los códigos. Finalmente, deberá configurar su código de visualización para interpretar los valores como enteros sin signo de 8 bits.
Enviar medidas de mayor resolución es complicado
Un enfoque ingenuo es escribir un código de envío que envíe cada byte del valor en secuencia, recordando asegurarse de que todos los bytes provengan de la misma lectura de ADC. Pero este enfoque es ingenuo porque no proporciona ningún mecanismo para sincronizar el transmisor y el receptor. Si el receptor aún no está escuchando antes de que se inicie el transmisor, o si alguna vez se pierde un byte debido a un error, entonces el receptor puede comenzar a combinar bytes de diferentes lecturas en orden desplazado para generar valores falsos. Es posible solucionar esto con códigos de inicio especiales prohibidos que luego se escapan si aparecen en los datos, encabezados periódicos de resincronización multibyte de valores mágicos, o de forma menos confiableintervalos de tiempo entre lecturas (lo que es especialmente imprudente si alguna vez se utilizan convertidores de serie USB).
0b011mmmmm
desea 0b010nnnnn
enviar 0bmmmmmnnnnn
. . Sin embargo, esto necesitaría un software de recepción personalizado que busque un comienzo de lectura 0b011xxxxx
y un siguiente comienzo 0b010xxxxx
y recombinación de los bits apropiados de cada uno. (El bit 0x40 está activado en cada caso con la esperanza de dirigir los códigos al rango imprimible, tal vez preservando la posibilidad de usar herramientas de línea de comandos para manipular los datos)Pero, en general, esto genera muchos dolores de cabeza, por lo que primero vale la pena ver si un enfoque no binario podría ser viable.
Envíe cadenas legibles por humanos codificando la representación ASCII de dígitos con un espacio o delimitador de nueva línea. Debido a que el carácter delimitador no se puede confundir con un dígito numérico, proporciona una división fácil de las lecturas y una sincronización implícita. Incluso en el caso de un error, el impacto no persistirá sino que se limitará a una o dos lecturas sucesivas. El uso de una nueva línea suele ser la mejor opción, ya que muchos esquemas de procesamiento de secuencias tienen un búfer de línea natural, un comportamiento que puede tener que cambiarse explícitamente para evitar demoras cuando se usa un delimitador diferente.
Los valores decimales se interpretan más fácilmente a simple vista y pueden estar bien para su propósito, pero deberá rellenar con ceros o aceptar que la cantidad de caracteres por valor es variable.
Los valores hexadecimales (rellenados con ceros a la izquierda) producen una conversión limpia de tamaño fijo de los datos. El valor en sí será exactamente el doble de la longitud del valor binario, es decir, 16 bits se convierten en 4 caracteres de 8 bits o 32 bits, pero el delimitador también tomará un byte, por lo que un valor de 16 bits requiere cinco caracteres en total. Dado que la resolución de su fuente es de 10 bits, en realidad podría usar solo 3 dígitos hexadecimales y un delimitador, para un total de 4 caracteres por lectura. Por supuesto, el software que consume necesitará interpretar los valores hexadecimales, pero esto tiende a funcionar bien con la mayoría de las cosas que ejecutaría en Linux; aún así, resolverlo en algunos contextos puede ser un poco complicado, puede requerir inyectar un prefijo "0x" antes de interpretar la cadena recibida, etc.
Hay algunos problemas potenciales adicionales en el caso de que intente utilizar un retraso programático fijo pero nervioso para regular dos cosas que tienen condiciones de preparación explícitas más apropiadas.
No puede verificar que el UART serial esté listo para nuevos datos antes de escribir en el registro de datos.
debe consultar la hoja de datos para determinar si es legítimo leer el registro de conversión de ADC cuando una conversión no está completa; de lo contrario, debe esperar el indicador de conversión completa.
Está enviando bytes únicos sin firmar a través de la serie.
El comando correcto que necesitas es:
od -t u1 /dev/ttyACM3 -w1
lo que significa que el tipo de sus datos es un entero sin signo de un byte de ancho, mientras que w1 debería imprimir un byte por línea.
Además, en su línea:
UDR0 = (ADCW*100/1024) ;
No está usando value
, que lanzó correctamente a int sin firmar . No estoy seguro de lo que hace el compilador, pero usar value
en esa línea sería más claro.
Utilizar el
od -t d2 /dev/ttyACM3 -w2
o
od -s /dev/ttyACM3 -w2
comando en su lugar.
Según man od, el -d
conmutador muestra enteros sin signo de 2 bytes . -s
o -t d2
mostrará los firmados.
Cambiando la línea
UDR0 = ( ADCW * 100 / ( 1024 ) );
A
UDR0 = ( ADCW * 10 / ( 1024 ) );
Y llamando al comando:
od -t d4 /dev/ttyACM2 -w4
Me dio el valor correcto en un rango de 0 a 50 529 027, donde 0 es oscuridad total. El único problema es que los 'pasos' se actualizan solo cada 4 cm, donde normalmente debería poder detectar la diferencia en un par de milímetros.
vladimir cravero
K666
Marca
vladimir cravero
#define ADCW ((ADCH<<8)|ADCL)
? Por otra parte, configura el noveno bit para que siempre sea 1 y luego configura la serie en modo de 8 bits. Y, por último, espera que los datos estén en el rango de 0 a 1023, peroADCW*100/1024
los datos deberían estar en el rango de 0 a 100 y encajar en 8 bits. Aquí falta algo...K666
vladimir cravero
od
supongo que esto se debe completamente a cómo funciona.Eugenio Sh.
vladimir cravero
UDR0 = whatever
línea. Él está escribiendo los 8 lsbs y luego el ISR los envía, luego los interpreta como entradas con signo de 16 bits, por lo tanto, la basura que está viendo. Esto también explica por qué el valor se ajusta muchas veces, si el rango de 10 bits se usa por completo, esperaría que se ajustara cada vez que cambia el bit 7, y esto sucede 2 ^ 10/2 ^ 8 = 4 veces.vladimir cravero
value
nunca se usa.