Errores al transmitir un número de 10 bits leído con Atmega328 a través de una serie y leerlo con "od"

1

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 la 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();
}

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 raro, si quito 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" ¿no significa que es un 10bit (16bit) en lugar de 8bit?

    
pregunta K666

4 respuestas

2

No puede pasar un valor de múltiples bytes a través de un canal serie de 8 bits en una operación única . 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 malinterpreta 2 o 4 de ellos en una fila como valores de múltiples bytes falsos.

Si puede tolerar 256 valores distintos , puede modificar su código para desplazar a la derecha el valor de conversión (u operar en modo de 8 bits para iniciar), de modo que envíe un byte único legítimo sin firmar. medición. A continuación, deberá asegurarse de que el dispositivo serie Linux esté funcionando en modo completamente crudo, de modo que no se den interpretaciones especiales a los códigos. Finalmente, deberá configurar el código de visualización para interpretar los valores como enteros sin signo de 8 bits.

Enviar mediciones de mayor resolución es complicado

  • Un enfoque ingenuo es escribir el código de envío que envía cada byte del valor en secuencia, recordando asegurarse de que todos los bytes provienen de la lectura del ADC same . Pero este enfoque es ingenuo porque no proporciona ningún mecanismo para sincronizar el transmisor y el receptor. Si el receptor no está escuchando antes de que se inicie el transmisor, o si alguna vez se cae un byte debido a un error, el receptor puede comenzar a combinar bytes de diferentes lecturas en orden desplazado para generar valores falsos. Es es posible solucionar esto con códigos de inicio prohibidos especiales que luego se escapan si aparecen en datos, encabezados de resincronización multi-byte de valores mágicos periódicos, o menos intervalos de tiempo entre las lecturas (lo cual es especialmente imprudente si alguna vez se pueden usar convertidores USB en serie).

    • En el caso específico donde sus lecturas son de 10 bits, podría hacer algo como dividirlas en dos palabras de 5 bits y usar los bits altos de cada uno como un código de campo, por ejemplo, 0b011mmmmm y 0b010nnnnn donde 0bmmmmmnnnnn es un valor de 10 bits que desea enviar. Sin embargo, esto necesitaría un software de recepción personalizado que busque una lectura que comience 0b011xxxxx y una siguiente que comience 0b010xxxxx y que combine 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, quizás preservando la posibilidad de usar herramientas de línea de comandos para manipular los datos)

Pero en general esto crea muchos dolores de cabeza, por lo que vale la pena ver primero si un enfoque no binario podría ser viable.

  • Enviar cadenas humanas legibles que codifican la representación ASCII de dígitos con un espacio o un delimitador de nueva línea. Debido a que el carácter delimitador no se puede confundir con un dígito numérico, proporciona una fácil división de las lecturas y la sincronización implícita. Incluso en el caso de un error, el impacto no persistirá, pero 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 flujo son naturalmente búferes de línea, 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 tendrá que rellenar el teclado o aceptar que el número 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 general. Dado que la resolución de la 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 suele funcionar bien con la mayoría de las cosas que se ejecutan en Linux; aún así, descifrarlo 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 hecho de que intenta utilizar un retraso programático fijo pero inestable para regular dos cosas que tienen condiciones listas explícitas más apropiadas.

  • No está verificando que el UART serie esté listo para nuevos datos antes de escribir en el registro de datos.

  • debe revisar la hoja de datos para determinar si es legítimo leer el registro de conversión ADC cuando la conversión no está completa; de lo contrario, debe esperar a que aparezca el indicador de conversión completa.

respondido por el Chris Stratton
1

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 entero sin signo de un byte de ancho, mientras que w1 debería imprimir un byte por línea.

También, en tu línea:

UDR0 = (ADCW*100/1024)  ;

No estás utilizando value , que has convertido correctamente en unsigned int. No estoy seguro de lo que hace el compilador, pero usar value en esa línea sería más claro.

    
respondido por el Vladimir Cravero
-1

Usa el

od -t d2 /dev/ttyACM3 -w2

o

od -s /dev/ttyACM3 -w2

comando en su lugar.

Según man od el -d switch muestra 2 enteros de sin signo . -s o -t d2 mostrarán los firmados.

    
respondido por el Eugene Sh.
-1

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 completa. 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.

    
respondido por el K666

Lea otras preguntas en las etiquetas