Επιφάνειες αφής (Touch Screens). Μέρος 2o.

Σε προηγούμενο ΆΡΘΡΟ πως λειτουργούν οι επιφάνειες αφής ωμικού τύπου και πως θα μπορούσε ένας μικροελεγκτής να διαβάσει τις συντεταγμένες του σημείου επαφής με την επιφάνεια. Θα δούμε τώρα πως υλοποιείται προγραμματιστικά σε έναν AVR μα γλώσσα C. Το κύκλωμα είναι πολύ απλό. Απλά συνδέουμε τα τέσερα καλώδια της επιφάνειας αφής με το Arduino. To Xa στο Α0 (PC0 του ATmega328p) το Xb στο 8 (PΒ0 του ATmega328p), το Xb στο Α1 (PC1 του ATmega328p) το Xb στο 9 (PΒ1 του ATmega328p).

DSC02588 DSC02590

Ο κώδικας είναι αρκετά απλός και τα σχόλια μπόλικα. Το μόνο που κάνει το πρόγραμμα που τρέχει στον ATega328p είναι να εντοπίζει τις συντεταγμένες του σημείου επαφής και να τις στέλνει μέσω της σειριακής πόρτας στον υπολογιστή. Εκεί μπορεί να της διαβάσει οποιοδήποτε πρόγραμμα που ανοίγει σειριακές θύρες. Στην περίπτωση μου, ένα απλό πρόγραμμα σε Python, που απλά τυπώνει στην στάνταρ έξοδο (stdout). Μετά μπορούμε να κάνουμε ό,τι θέλουμε.

Όλα τα αρχεία απαραίτητα αρχεία μπορείτε να τα βρείτε ΕΔΩ.

#include <avr/io.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <stdio.h>
#include <avr/interrupt.h>

#include "uart.h"

/* Υπολογισμοί για την ταχύτητα δεδομένων με τον υπολογιστή. */
#define BAUD 9600UL

#define UBRR_VAL ((F_CPU+BAUD*8)/(BAUD*16)-1) // Στρογγυλοποίηση.
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))   // Πραγματικά Baud.
#define BAUD_ERROR ((BAUD_REAL*1000)/BAUD)    // Σφάλμα ανά 1000 μέρη.

// Αν το σφάλμα είναι μεγαλύτερο του 1% σταματά η μεταγλώττιση του κώδικα.
#if ((BAUD_ERROR<990) || (BAUD_ERROR>1010))
    #error To sfalma ston ry8mo Baud einai megalytero toy 1%, mataiosh!
#endif

// -------------------Αποστολή της ροής χαρακτήρων στο UART ----------------
// Ρύθμιση της συνάρτησης UARTsendChar() για συνεχή ροή (stream).
static int UARTsendstream(char c, FILE *stream);

static FILE uart_str = FDEV_SETUP_STREAM(UARTsendstream, NULL, _FDEV_SETUP_WRITE);

static int UARTsendstream(char c , FILE *stream){
    uart_putc(c);
    return 0;
}
// -------------------------------------------------------------------------

// Η αντίσταση μεταξύ των δύο καλωδίων της οθόνης (Xa, Xb).
#define R_X_TOTAL 875

// Καθορισμός των pin των καλωδίων του άξονα Χ.
#define Xa PC0
#define DDRXa DDRC
#define PORTXa PORTC

#define Xb PB0
#define DDRXb DDRB
#define PORTXb PORTB

// Καθορισμός των pin των καλωδίων του άξονα Y.
#define Ya PC1
#define DDRYa DDRC
#define PORTYa PORTC

#define Yb PB1
#define DDRYb DDRB
#define PORTYb PORTB

// Διάσταση άξονα Χ σε χιλιοστά.
#define LENGTH 72

// Διάσταση άξονα Υ σε χιλιοστά.
#define HEIGHT 41

uint16_t read_ADC(uint8_t);
void adc_init(void);
uint16_t readX(void);
uint16_t readY(void);
int readZ(void);

int main(void){
  
    // Initializing ADC.
    adc_init(); 
    
     // Αρχικοποίηση της ροής των χαρακτήρων.
    stdout = &uart_str;
    uart_init(UART_BAUD_SELECT(BAUD,F_CPU));    // Αρχικοποίηση του UART.
    
    // Ενεργοποίηση των καθολικών Διακοπών (Interrupts).
    sei();
    
    while(1){
      
      // Έλεγχος για τον αν υπάρχει σημείο επαφής με την ανάγνωση του άξονα Ζ.
      if (readZ()>300){
	// Μετατροπή των τιμών του ADC σε χιλιοστά και απποστολή.
	printf("%d ", (uint16_t) (LENGTH * (readX()/1024.0)));
	printf("%d\n", (uint16_t) (HEIGHT * (readY()/1024.0)));
      };
     
      _delay_ms(50);
     
    };

    return 0;
}

uint16_t readX(void) {
  
   // Ya ως Είσοδος.
   DDRYa &= ~((1<<Ya));
   // Ya σε υψηλή σύνθετη αντίσταση, High-Z.
   PORTYa &= ~((1<<Ya));
   // Υb ως Είσοδος.
   DDRYb &= ~((1<<Yb));
   // Yb σε υψηλή σύνθετη αντίσταση, High-Z.
   PORTYb &= ~((1<<Yb));
   
   // Xa ως Έξοδος.
   DDRXa |= (1<<Xa);
   // Xa σε λογικό 1 (τάση Vcc).
   PORTXa |= (1<<Xa);
   // Xb ως Έξοδος.
   DDRXb |= (1<<Xb);
   // Xb σε λογικό 0 (GND).
   PORTXb &= ~((1<<Xb));
   
   // Λίγη καθυστέρηση για να ισορροπήσουν οι είσοδοι/έξοδοι.
   _delay_ms(10);
   
   return (read_ADC(Ya));
}

uint16_t readY(void) {
  
   // Xa ως Είσοδος.
   DDRXa &= ~((1<<Xa));
   // Χa σε υψηλή σύνθετη αντίσταση, High-Z.
   PORTXa &= ~((1<<Xa));
   // Xb ως Είσοδος.
   DDRXb &= ~((1<<Xb));
   // Xb σε υψηλή σύνθετη αντίσταση, High-Z.
   PORTXb &= ~((1<<Xb));
   
   // Ya ως έξοδος.
   DDRYa |= (1<<Ya);
   // Υa σε λογικό 1 (τάση Vcc).
   PORTYa |= (1<<Ya);
   // Yb ως έξοδος;
   DDRYb |= (1<<Yb);
   // Yb σε λογικό 0 (τάση GND).
   PORTYb &= ~((1<<Yb));
   
   _delay_ms(10);
   
   return (read_ADC(Xa));
}

int readZ(void) {
  
   // Xb ως Έξοδος.
   DDRXb |= (1<<Xb);
   // Xb σε λογικό 0 (GND).
   PORTXb &= ~((1<<Xb));
  
   // Yb ως έξοδος;
   DDRYb |= (1<<Yb);
   // Yb σε λογικό 0 (τάση Vcc).
   PORTYb |= (1<<Yb);
  
  // Xa ως Είσοδος.
   DDRXa &= ~((1<<Xa));
  // Xa σε υψηλή σύνθετη αντίσταση, High-Z.
   PORTXa &= ~((1<<Xa));
   // Υa ως Είσοδος.
   DDRYa &= ~((1<<Ya));
   // Ya σε υψηλή σύνθετη αντίσταση, High-Z.
   PORTYa &= ~((1<<Ya));

    _delay_ms(10);
  
    uint16_t z1 = read_ADC(Xa); 
    uint16_t z2 = read_ADC(Ya);

    // Μεταβλητή για την τιμή της αντίστασης επαφής.
    double rtouch;
    
    rtouch = z2;
    rtouch = rtouch/z1;
    rtouch = rtouch-1;
    rtouch = rtouch * readX();
    rtouch = rtouch * R_X_TOTAL;
    rtouch = rtouch / 1024;
    
    return rtouch;
}

uint16_t read_ADC(uint8_t ADC_channel){
  
    int16_t adc_val;
    ADMUX = (ADMUX & 0xF0) | (ADC_channel & 0x0F);
    ADCSRA |= (1<<ADSC);           // Έναρξη της μετατροπής.
    while( ADCSRA & (1<<ADSC) );   // Αναμονή για ολοκλήρωση της μετατροπής.
    adc_val = ADC;
    return adc_val;
}

void adc_init(void){

    // Ρύθμιση του ADC σε εφάπαξ (single shot) ρυθμό λειτουργίας.
    // Σελίδα 262,263,264 της τεκμηρίωσης του ATmega328p.

    ADMUX |= (1<<REFS0);                         // AVCC με εξωτερικό πυκνωτή στο AREF.
    ADCSRA |= (1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);  // Prescaler 128 (16 MHz/64=125KHz).
    ADCSRA |= (1<<ADEN);                         // Ενεργοποίηση του ADC.
}
This entry was posted in AVR, Electronics, Science and tagged , , , , , , , , , , , , , . Bookmark the permalink.

Leave a comment