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