[LINUX] L Chika avec Sipeed Lichee Zero (opération GPIO)

Aperçu

J'ai essayé de lire L Chika et de passer avec Sipeed Lichee Zero. Peut-être que le Richee Nano peut faire la même chose, non?

Méthode

Il existe à peu près deux façons d'accéder à GPIO: en utilisant un fichier de périphérique et en accédant à la mémoire. L'utilisation des fichiers de l'appareil est simple mais lente. La méthode d'accès à la mémoire et de fonctionnement est rapide, mais elle est assez gênante. Il y a des avantages et des inconvénients.

Méthode 1 Utiliser un fichier de périphérique

Les LED polychromes de la carte sont connectées à PG0 et PG2. Si vous accédez au fichier de l'appareil avec le numéro 192 à 194 correspondant à ce numéro, la LED couleur s'allumera. Cependant, comme cette anode LED est commune, elle s'allume à 0 et s'éteint à 1. En premier lieu, le port devient 0 par initialisation, donc il s'allume après l'initialisation.

# echo 192 > /sys/class/gpio/export
# echo out > /sys/class/gpio/gpio192/direction 
# echo 1 > /sys/class/gpio/gpio192/value
# echo 0 > /sys/class/gpio/gpio192/value

Si vous souhaitez lire la valeur du commutateur connecté à PG3, procédez comme suit et 0 ou 1 sera renvoyé. (L'interrupteur nécessite une résistance pull-up.)

# echo 195 > /sys/class/gpio/export
# echo in > /sys/class/gpio/gpio195/direction 
# cat /sys/class/gpio/gpio195/value

La correspondance entre le numéro de port et le numéro de fichier de l'appareil est écrite dans le document officiel, vous pouvez donc le vérifier. http://zero.lichee.pro/%E9%A9%B1%E5%8A%A8/GPIO_file.html

Méthode 2 Utiliser l'accès à la mémoire

Pour autant que j'ai lu la documentation officielle, il est dit qu'il existe une bibliothèque qui peut faire fonctionner GPIO de cette manière, mais je ne l'ai trouvée nulle part. Probablement pas parce que la communauté est arrivée à la conclusion qu'il n'y avait pas de question similaire il y a quelques années. Alors je l'ai fait parce que je le voulais, mais c'était assez difficile parce que c'était la première fois. Je n'ai pas beaucoup d'informations ... Les seules fonctions sont l'initialisation / réglage du port et l'entrée / sortie. Les interruptions ne sont pas implémentées. Comme le montant est petit, j'ai tout écrit dans le fichier d'en-tête.

test_io.h


#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

/*Adresse physique du registre périphérique(D'après les spécifications de BCM2835) */
#define REG_ADDR_BASE        (0x01C20800)       /* bcm_host_get_peripheral_address()Est mieux*/
#define REG_ADDR_GPIO_BASE 0x1C20000
#define REG_ADDR_GPIO_LENGTH 0x1000
#define PORT_OFFSET 0x800
#define REG_ADDR_GPIO_GPFSEL_0 0x0000 + PORT_OFFSET
#define REG_ADDR_GPIO_OUTPUT_DATA_0 0x10 + PORT_OFFSET

#define REG(addr) (*((volatile unsigned int*)(addr)))
#define DUMP_REG(addr) printf("DUMP = %08X\n", REG(addr));

#define IN 0
#define OUT 1
#define DISABLE 2

#ifndef LICHEEIO_H
#define LICHEEIO_H

int io_init(void);
int io_release(void);
int port_no_check(int port, int pin);
int setup_gpio(char *pin_no, int io_set);
int output_gpio(char *pin_no, int hl_set);
int input_gpio(char *pin_no);

#endif /* LICHEEIO_H */


int address;    /*Adresse virtuelle au registre GPIO(Espace utilisateur) */
int fd;


int io_init(void){
    /*Ouvrir le fichier de l'appareil pour accéder à la mémoire*/
    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        perror("open");
        return -1;
    }

    // long sz = sysconf(_SC_PAGESIZE);
    // printf("%08X", sz);

    /* ARM(CPU)Adresse physique vue de → Mappage vers une adresse virtuelle*/
    address = (int)mmap(0, REG_ADDR_GPIO_LENGTH, 
                        PROT_READ | PROT_WRITE, MAP_SHARED, 
                        fd, REG_ADDR_GPIO_BASE);
    if (address == (int)MAP_FAILED) {
        perror("mmap");
        close(fd);
        return -1;
    }

    return 0;
}

int io_release(void){
    /*Libérer les ressources utilisées*/
    munmap((void*)address, REG_ADDR_GPIO_LENGTH);
    close(fd);
    return(0);
}


int port_no_check(int port, int pin){
    int err_F = 0;
    switch (port){
    case 1:
        if(pin < 0 || pin > 9)
            err_F = 1; 
        break;
    case 2:
        if(pin < 0 || pin > 3)
            err_F = 1; 
        break;
    case 4:
        if(pin < 0 || pin > 24)
            err_F = 1; 
        break;
    case 5:
        if(pin < 0 || pin > 6)
            err_F = 1; 
        break;
    case 6:
        if(pin < 0 || pin > 5)
            err_F = 1; 
        break;
    default:
        err_F = 1; 
        break;
    }
    return(err_F);
}


int setup_gpio(char *pin_no, int io_set){
    int err_F = 0;
    int port ,pin, reg_no;
    port = pin_no[0] - 'A';
    
    pin = atoi(pin_no + 1);
    if(port_no_check(port, pin) == 1){
        printf("errno");
        return(-1);
    }
    reg_no = pin / 8;
    if(io_set == IN){
        REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24 + reg_no) &= ~(0x07 << pin * 4);
    }
    else if (io_set == OUT){
        REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24 + reg_no) |= (0x1 << pin * 4);
        REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24 + reg_no) &= ~(0x6 << pin * 4);
        //REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24) = 0x77777777;
    }
    else if (io_set == DISABLE){
        REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24 + reg_no) |= (0x07 << pin * 4);
    }
    //DUMP_REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24);
    return(0);
}


int output_gpio(char *pin_no, int hl_set){
    int port, pin;
    port = pin_no[0] - 'A';
    pin = atoi(pin_no + 1);
    if(port_no_check(port, pin) == 1){
        printf("errno");
        return(-1);
    }
    if(hl_set == 0){ //in
        REG(address + REG_ADDR_GPIO_OUTPUT_DATA_0 + port * 0x24) &= ~(0x1 << pin);
    }
    else if (hl_set == 1){ //out
        REG(address + REG_ADDR_GPIO_OUTPUT_DATA_0 + port * 0x24) |= (0x1 << pin);
    }
    //DUMP_REG(address + REG_ADDR_GPIO_OUTPUT_DATA_0 + port * 0x24);
    return(0);
}


int input_gpio(char *pin_no){
    int port, pin, data;
    port = pin_no[0] - 'A';
    pin = atoi(pin_no + 1);
    if(port_no_check(port, pin) == 1){
        printf("errno");
        return(-1);
    }
    
    data = REG(address + REG_ADDR_GPIO_OUTPUT_DATA_0 + port * 0x24) & (0x1 << pin);
    //DUMP_REG(address + REG_ADDR_GPIO_OUTPUT_DATA_0 + port * 0x24);
    if(data != 0){
        data = 1;
    }
    //printf("data = %08X\n", data);
    return(data);
}

Un exemple de programme dans lequel le bleu et le vert clignotent en alternance.

Ltika.c


#include "test_io.h"
#include <unistd.h>

int main(){
    int sta, data;
    printf("%d\n", io_init());

    sta += setup_gpio("G0", OUT);
    sta += setup_gpio("G1", OUT);
    printf("sta=%d\n", sta);

    while (1){
        output_gpio("G0", 0);
        output_gpio("G1", 1);
        usleep(1e5);
        output_gpio("G1", 0);
        output_gpio("G0", 1);
        usleep(1e5);
    }
    io_release();
}

Compiler / exécuter

gcc Ltika.c -o Ltika -lm -std=gnu99
sudo ./Ltika

Exemple de programme qui s'allume lorsque l'interrupteur connecté à PG3 est enfoncé (L'interrupteur nécessite une résistance pull-up.)

sw.c


#include "test_io.h"
#include <unistd.h>

int main(){
    int sta, data;
    printf("%d\n", io_init());

    sta = setup_gpio("G0", OUT);
    sta = setup_gpio("G1", DISABLE);
    //sta = setup_gpio("G2", OUT);
    sta = setup_gpio("G3", IN);
    printf("%d\n", sta);
    //output_gpio("G1", 1);
    //output_gpio("G2", 1);
    
    while(1){
        data = input_gpio("G3");
        output_gpio("G0", data);
        usleep(1e3);
    }
    io_release();
}

Compiler / exécuter

gcc sw.c -o sw -lm -std=gnu99
sudo ./sw

De plus, à part ce programme PG, je n'ai pas encore testé en connectant des pièces, donc cela peut ne pas fonctionner correctement. J'ai confirmé dans une certaine mesure si l'état du registre a changé, mais ...

prime

Ce programme est un programme d'affichage de liste d'état pour les registres liés aux ports.

pin_status.c


#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>

/*Adresse physique du registre périphérique(D'après les spécifications de BCM2835) */
#define REG_ADDR_BASE        (0x01C20800)       /* bcm_host_get_peripheral_address()Est mieux*/
#define REG_ADDR_GPIO_BASE 0x1C20000
#define REG_ADDR_GPIO_LENGTH 0x1000
#define PORT_OFFSET 0x800
#define REG_ADDR_GPIO_GPFSEL_0 0x0000 + PORT_OFFSET

#define REG(addr) (*((volatile unsigned int*)(addr)))
#define DUMP_REG(addr) printf("DUMP = %08X\n", REG(addr));



int address;    /*Adresse virtuelle au registre GPIO(Espace utilisateur) */
int fd;


int io_init(void){
    /*Ouvrir le fichier de l'appareil pour accéder à la mémoire*/
    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        perror("open");
        return -1;
    }

    // long sz = sysconf(_SC_PAGESIZE);
    // printf("%08X", sz);

    /* ARM(CPU)Adresse physique vue de → Mappage vers une adresse virtuelle*/
    address = (int)mmap(0, REG_ADDR_GPIO_LENGTH, 
                        PROT_READ | PROT_WRITE, MAP_SHARED, 
                        fd, REG_ADDR_GPIO_BASE);
    if (address == (int)MAP_FAILED) {
        perror("mmap");
        close(fd);
        return -1;
    }

    return 0;
}

int io_release(void){
    /*Libérer les ressources utilisées*/
    munmap((void*)address, REG_ADDR_GPIO_LENGTH);
    close(fd);
    return(0);
}



// int setup_gpio(char *pin_no, int io_set){
//     int err_F = 0;
//     int port ,pin;
//     port = pin_no[0] - 'A';
    
//     pin = atoi(pin_no + 1);
//     if(port_no_check(port, pin) == 1){
//         printf("errno");
//         return(-1);
//     }
//     if(io_set == IN){
//         REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24) &= ~(0x07 << pin * 4);
//     }
//     else if (io_set == OUT){
//         REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24) |= (0x1 << pin * 4);
//         REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24) &= ~(0x6 << pin * 4);
//         //REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24) = 0x77777777;
//     }
//     else if (io_set == DISABLE){
//         REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24) |= (0x07 << pin * 4);
//     }
//     //DUMP_REG(address + REG_ADDR_GPIO_GPFSEL_0 + port * 0x24);
//     return(0);
// }


int bit2int(int *bit_data, int *int_data){
    for(int i = 0; i < 4; i++){
        int mask = 0x0007, x;
        for(int j = 0; j < 8; j++){
            x = bit_data[i] & mask;
            //printf("%08x  ", x);
            //printf("%08x  ", x >> j * 4);
            int_data[i * 8 + j] = x >> j * 4;
            //printf("mask = %08X\n", mask);
            mask = mask << 4;
        }
    }
    //printf("\n");
}

int main(){
    io_init();
    int read_data[4];
    int port_status[5][32]; 
    for(int i = 0; i < 5; i++){
        if(i == 0){
            read_data[0] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 1 * 0x24);
            read_data[1] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 1 * 0x24 + 1);
            bit2int(read_data, port_status[0]);
        }
        if(i == 1){
            read_data[0] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 2 * 0x24);
            bit2int(read_data, port_status[1]);
        }
        if(i == 2){
            read_data[0] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 4 * 0x24);
            read_data[1] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 4 * 0x24 + 1);
            read_data[2] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 4 * 0x24 + 2);
            read_data[3] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 4 * 0x24 + 3);
            bit2int(read_data, port_status[2]);
        }
        if(i == 3){
            read_data[0] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 5 * 0x24);
            bit2int(read_data, port_status[3]);
        }
        if(i == 4){
            read_data[0] = REG(address + REG_ADDR_GPIO_GPFSEL_0 + 6 * 0x24);
            bit2int(read_data, port_status[4]);
        }


    }
    for(int i = 0; i < 5; i++){
        int indention = 7;
        if(i == 0){
            printf("PB\n");
        }
        else if(i == 1){
            printf("PC\n");
        }
        else if(i == 2){
            printf("PE\n");
        }
        else if(i == 3){
            printf("PF\n");
        }
        else if(i == 4){
            printf("PG\n");
        }
        for(int j = 0; j < 32; j++){            
            if(port_status[i][j] == 7){
                printf("%02d:Invalide", j);
            }
            else if(port_status[i][j] == 0){
                printf("%02d:contribution", j);
            }
            else if(port_status[i][j] == 1){
                printf("%02d:production", j);
            }
            else{
                printf("%02d:autre%02d  ", j, port_status[i][j]);
            }
            if((i == 0 && j == 9) || (i == 1 && j == 3) || 
               (i == 2 && j == 24) || (i == 3 && j == 6) || 
               (i == 4 && j == 5)){
                   break;
               }
            if(j == indention){
                printf("\n");
                indention += 8;
            }
            
        }
        printf("\n\n");
    }


    io_release();
}
    

Matériel de référence

Document officiel: GPIO [Fiche technique de la puce principale] (https://linux-sunxi.org/images/2/23/Allwinner_V3s_Datasheet_V1.0.pdf) [Schéma du circuit Sipeed Lichee Zero] (https://dl.sipeed.com/LICHEE/Zero/HDK/lichee_zero.pdf) Comment créer un pilote de périphérique Linux intégré (5)

Recommended Posts

L Chika avec Sipeed Lichee Zero (opération GPIO)
Remote L Chika avec pigpio
Essayez L Chika avec raspberrypi
Connectez Sipeed Lichee Zero au filet
RaspberryPi L Chika avec Python et C #
[C, C ++, Python, JavaScript] L Chika avec Edison