Eigener Treiber



  • Hallo,

    ich habe mir einen I²C Treiber für einen A/D Wandler geschrieben (bzw. bin noch dabei).

    #include <linux/fs.h>
    #include <linux/version.h>
    #include <linux/module.h>
    #include <linux/init.h>
    #include <asm/uaccess.h>  
    #include <linux/ioctl.h>
    #include <asm/io.h>
    
    // Peripherieinformationen  
    #define Peripherie_Basis	0x20000000													// Startadresse der Peripherie						
    #define GPIO_Basis			(Peripherie_Basis + 0x200000)								// Startadresse des GPIO-Controllers
    #define I2C1_Basis			(Peripherie_Basis + 0x804000)								// Startadresse des I2C-Controllers
    
    // Treiberinformationen
    #define NAME				"ADS1015"													// Name des Treibers
    #define DRIVER_MAJOR 		240															// Major Nummer
    
    // BSC Status
    #define DONE 				(1 << 1)
    
    // ADC
    #define ADC_Data 			0
    #define ADC_Config 			1
    #define LO_Tresh			2
    #define HI_Tresh			3
    
    static volatile uint32_t Bus = 1;
    static volatile uint32_t Address = 0x48;
    static volatile uint32_t Config = 0x40E3;
    
    // Programmierer
    MODULE_AUTHOR("Daniel Kampert");
    MODULE_LICENSE("GPL");
    MODULE_DESCRIPTION("Driver for ADS1015 I2C ADC");
    MODULE_SUPPORTED_DEVICE("Raspberry Pi 512MB");       
    
    static volatile uint32_t  *GPIO; 
    static volatile uint32_t  *I2C1; 
    
    void I2C_Send_SingleByte(int Bus, int Data, int Address)
    {
    	// Adresse speichern
    	*(I2C1 + 3)  = Address;
    
    	// Anzahl zu übermittelnder Bytes festlegen
    	*(I2C1 + 2) = 1; 
    
    	// Daten speichern
    	*(I2C1 + 4) = Data;
    
    	// Statusbits löschen
    	*(I2C1 + 1) = (1 << 9) | (1 << 8) | (1 << 1);
    
    	// Übertragung starten
    	*(I2C1) = (1 << 15) | (1 << 7); 	
    	while(!(*(I2C1 + 1) & DONE));
    }
    
    void I2C_Send_Byte(int Bus, int Register, int Data, int Address)
    {
    	// Adresse speichern
    	*(I2C1 + 3)  = Address;
    
    	// Anzahl zu übermittelnder Bytes festlegen
    	*(I2C1 + 2) = 2; 
    
    	// Daten speichern
    	*(I2C1 + 4) = Register;
    	*(I2C1 + 4) = Data;
    
    	// Statusbits löschen
    	*(I2C1 + 1) = (1 << 9) | (1 << 8) | (1 << 1);
    
    	// Übertragung starten
    	*(I2C1) = (1 << 15) | (1 << 7); 	
    	while(!(*(I2C1 + 1) & DONE));
    }
    
    void I2C_Send_Short(int Bus, int Register, int Data, int Address)
    {
    	int MSB = 0;
    	int LSB = 0;
    
    	// Daten aufteilen
    	MSB = Data & 0xFF00;
    	MSB = MSB >> 8;
    	LSB = Data & 0xFF;
    
    	// Adresse speichern
    	*(I2C1 + 3)  = Address;
    
    	// Anzahl zu übermittelnder Bytes festlegen
    	*(I2C1 + 2) = 3; 
    
    	// Daten im FIFO speichern
    	*(I2C1 + 4) = Register;
    	*(I2C1 + 4) = MSB;
    	*(I2C1 + 4) = LSB;
    
    	// Statusbits löschen
    	*(I2C1 + 1) = (1 << 9) | (1 << 8) | (1 << 1);
    
    	// Übertragung starten
    	*(I2C1) = (1 << 15) | (1 << 7); 	
    	while(!(*(I2C1 + 1) & DONE));
    }
    
    int I2C_Read_Short(int Bus, int Register, int Address)
    {
    	int MSB = 0;
    	int LSB = 0;
    
    	// Registeradresse setzen
    	I2C_Send_SingleByte(Bus, Register, Address);
    
    	// Adresse speichern
    	*(I2C1 + 3)  = Address;
    
    	// Anzahl zu übermittelnder Bytes festlegen
    	*(I2C1 + 2) = 2; 
    
    	// Statusbits löschen
    	*(I2C1 + 1) = (1 << 9) | (1 << 8) | (1 << 1);
    
    	// Übertragung starten
    	*(I2C1) = (1 << 15) | (1 << 7) | (1 << 4) | (1 << 0); 	
    
    	// Auf Daten warten
    	while(!(*(I2C1 + 1) & DONE));
    
    	// Daten empfangen
    	MSB = (*(I2C1 + 4) & 255);
    	LSB = (*(I2C1 + 4) & 255);
    
    	return (MSB << 8) | LSB;
    }
    
    static int driver_open(struct inode *geraete_datei, struct file *instanz )
    {
    	// Speicherbereich der IO maskieren
    	GPIO = (uint32_t *)ioremap(GPIO_Basis, 4096);
    
    	// Speicherplatz des I2C Controllers maskieren
    	I2C1 = (uint32_t *)ioremap(I2C1_Basis, 4096);
    
    	// Bitmuster löschen
    	*(GPIO) &= ~(7 << 6);												
    	*(GPIO) &= ~(7 << 9);
    
    	// Alternative Funktion 1 aktivieren
    	*(GPIO) |= 4 << 6;
    	*(GPIO) |= 4 << 9;
    
    	// Systemlog Ausgabe
    	printk("Treiber geoeffnet\n");
    	printk("ADC wird konfiguriert\n");
    
    	// ADC konfigurieren
    	I2C_Send_Short(Bus, ADC_Config, Config, Address);
    
        return 0;
    }
    
    static int driver_close(struct inode *geraete_datei, struct file *instanz )
    {
    	printk(NAME);
        printk(" wird beendet....\n");
        return 0;
    }
    
    static ssize_t driver_read(struct file *instanz, char *User, size_t Count, loff_t *offset )
    {
    	int ret;
    	int Data = 0; 
    	int Konfiguration = 0;
    
    	Konfiguration = I2C_Read_Short(Bus, ADC_Config, Address);
    
    	// ADC auslesen
    	Data = I2C_Read_Short(Bus, ADC_Data, Address);
    	Data = Data >> 4;
    
    	// Ausgabe im Systemlog
    	printk("Konfiguration: %i\n", Konfiguration);
    	printk("ADC wird ausgelesen: %i\n", Data);
    
    	// Daten in den Userspace kopieren
    	ret = copy_to_user(User, &Data, Count);
    
    	return Count;	
    }
    
    static ssize_t driver_write(struct file *Instanz, const char *Buffer, size_t Count, loff_t *offs)
    {
    	return Count;
    }
    
    // Fileoperations
    static struct file_operations fops = {                     
        .owner= THIS_MODULE,
        .read= driver_read,
    	.write= driver_write,
        .open= driver_open, 
        .release= driver_close,
    };
    
    // Treiber beim Betriebssystem anmelden
    static int __init Init(void)
    {
        if(register_chrdev(DRIVER_MAJOR, NAME, &fops) == 0) 
    	{
    		printk(NAME);
    		printk(" erfolgreich angemeldet!\n");
            return 0;
    	}
    	else
    	{
    	    printk("Anmeldung fehlgeschlagen!\n");
    		return -EIO;
    	}
    }
    
    // Treiber vom Betriebssystem abmelden
    static void __exit Exit(void)
    {
        unregister_chrdev(DRIVER_MAJOR, NAME);  
    }
    
    module_init(Init);
    module_exit(Exit);
    

    Im Prinzip funktioniert er auch, allerdings nur wenn ich den Treiber über ein C-Programm auslese:

    #include <fcntl.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    
    int main(int argc, char** argv)               																
    {
    	int fd;
    	int Status;
    
    	fd = open("/dev/ADC", O_RDWR);
    	read(fd, &Status, 2);
    	printf("Status: %i", Status);
    	printf("\n");
    	sleep(1);
    
    	close (fd);	
    
    	return 0;
    }
    

    Wenn ich den Treiber nun aber in Python auslesen will, kommt da kein Wert wie 1625 raus, sondern immer ein Buchstabe (z.B. X).

    ADC = open("/dev/ADC", "r")
    
    ADC_W = 0
    ADC_W = ADC.read(2)
    
    print ADC_W
    

    Woran kann das liegen?
    Und dann habe ich noch eine weitere Frage.
    Bisher muss ich den Treiber immer über die Befehle

    insmod ADS1015_Driver.ko
    mknod /dev/ADC c 240 0

    einfügen und die Gerätedatei dafür anlegen.
    Gibt es eine Möglichkeit das über "modprobe" zu machen?
    Bzw. was ist der Unterschied zwischen den beiden Befehlen von mir und "modprobe"?

    Danke für die Hilfe!



  • Kampi schrieb:

    Woran kann das liegen?

    Das Python das ADC_W als Text auffasst.

    1625 = 0x659 -> 0x06 0x59 in ASCII ist das nicht druckbare Zeichen ACK und das 'Y'
    (bei 1624 hast du dann das 'X')

    Ich kenne Python nicht, aber nach kurzem googeln denke ich, dass es mit struct.unpack gehen sollte.


Log in to reply