Ich habe versucht, L Chika zu lesen und mit Sipeed Lichee Zero zu wechseln. Vielleicht kann der Lichee Nano das Gleiche tun, oder?
Es gibt ungefähr zwei Möglichkeiten, auf GPIO zuzugreifen: Verwenden einer Gerätedatei und Zugreifen auf Speicher. Die Verwendung von Gerätedateien ist einfach, aber langsam. Die Methode, auf den Speicher zuzugreifen und ihn zu bedienen, ist schnell, aber ziemlich mühsam. Es gibt Vor- und Nachteile.
Vollfarb-LEDs auf der Platine sind mit PG0 und PG2 verbunden. Wenn Sie auf die Gerätedatei mit den Nummern 192 bis 194 zugreifen, die dieser Nummer entsprechen, leuchtet die Vollfarb-LED auf. Da diese LED-Anode jedoch üblich ist, schaltet sie sich bei 0 ein und bei 1 aus. Erstens wird der Port durch Initialisierung zu 0, sodass er nach der Initialisierung aufleuchtet.
# 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
Wenn Sie den Wert des an PG3 angeschlossenen Schalters lesen möchten, gehen Sie wie folgt vor und 0 oder 1 wird zurückgegeben. (Der Schalter benötigt einen Pull-up-Widerstand.)
# echo 195 > /sys/class/gpio/export
# echo in > /sys/class/gpio/gpio195/direction
# cat /sys/class/gpio/gpio195/value
Die Entsprechung zwischen der Portnummer und der Gerätedateinummer wird in das offizielle Dokument geschrieben, sodass Sie sie überprüfen können. http://zero.lichee.pro/%E9%A9%B1%E5%8A%A8/GPIO_file.html
Soweit ich die offizielle Dokumentation gelesen habe, heißt es, dass es eine Bibliothek gibt, die GPIO auf diese Weise betreiben kann, aber ich konnte sie nirgendwo finden. Wahrscheinlich nicht, weil die Community zu dem Schluss gekommen ist, dass es vor einigen Jahren keine ähnliche Frage gab. Also habe ich es gemacht, weil ich es wollte, aber es war ziemlich schwierig, weil es das erste Mal war. Ich habe nicht viele Informationen ... Die einzigen Funktionen sind Portinitialisierung / -einstellung und Eingabe / Ausgabe. Interrupts sind nicht implementiert. Da der Betrag gering ist, habe ich alles in die Header-Datei geschrieben.
test_io.h
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
/*Physikalische Adresse des Peripherieregisters(Aus den Spezifikationen von BCM2835) */
#define REG_ADDR_BASE (0x01C20800) /* bcm_host_get_peripheral_address()Ist besser*/
#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; /*Virtuelle Adresse zum GPIO-Register(Benutzerraum) */
int fd;
int io_init(void){
/*Öffnen Sie die Gerätedatei für den Speicherzugriff*/
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
perror("open");
return -1;
}
// long sz = sysconf(_SC_PAGESIZE);
// printf("%08X", sz);
/* ARM(CPU)Physische Adresse von → Zuordnung zur virtuellen Adresse*/
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){
/*Geben Sie die verwendeten Ressourcen frei*/
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);
}
Ein Beispielprogramm, in dem Blau und Grün abwechselnd blinken.
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();
}
Kompilieren / ausführen
gcc Ltika.c -o Ltika -lm -std=gnu99
sudo ./Ltika
Beispielprogramm, das aufleuchtet, wenn der an PG3 angeschlossene Schalter gedrückt wird (Der Schalter benötigt einen Pull-up-Widerstand.)
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();
}
Kompilieren / ausführen
gcc sw.c -o sw -lm -std=gnu99
sudo ./sw
Abgesehen von diesem Programm PG habe ich es noch nicht getestet, indem ich Teile angeschlossen habe, sodass es möglicherweise nicht gut funktioniert. Ich habe bis zu einem gewissen Grad bestätigt, ob sich der Status des Registers geändert hat, aber ...
Dieses Programm ist ein Statuslisten-Anzeigeprogramm für portbezogene Register.
pin_status.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
/*Physikalische Adresse des Peripherieregisters(Aus den Spezifikationen von BCM2835) */
#define REG_ADDR_BASE (0x01C20800) /* bcm_host_get_peripheral_address()Ist besser*/
#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; /*Virtuelle Adresse zum GPIO-Register(Benutzerraum) */
int fd;
int io_init(void){
/*Öffnen Sie die Gerätedatei für den Speicherzugriff*/
if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
perror("open");
return -1;
}
// long sz = sysconf(_SC_PAGESIZE);
// printf("%08X", sz);
/* ARM(CPU)Physische Adresse von → Zuordnung zur virtuellen Adresse*/
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){
/*Geben Sie die verwendeten Ressourcen frei*/
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:Ungültig", j);
}
else if(port_status[i][j] == 0){
printf("%02d:Eingang", j);
}
else if(port_status[i][j] == 1){
printf("%02d:Ausgabe", j);
}
else{
printf("%02d:andere%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();
}
Offizielles Dokument: GPIO [Hauptchip-Datenblatt] (https://linux-sunxi.org/images/2/23/Allwinner_V3s_Datasheet_V1.0.pdf) [Sipeed Lichee Zero Schaltplan] (https://dl.sipeed.com/LICHEE/Zero/HDK/lichee_zero.pdf) So erstellen Sie einen eingebetteten Linux-Gerätetreiber (5)