[LINUX] L Chika with Sipeed Lichee Zero (GPIO operation)

Overview

I tried to read L Chika and Switch with Sipeed Lichee Zero. Perhaps the Lichee Nano can do the same thing, right?

Method

There are roughly two ways to access GPIO: using a device file and accessing memory. Using device files is easy but slow. The method of accessing the memory and operating it is quick, but it is quite troublesome. There are advantages and disadvantages.

Method 1 Use a device file

The full-color LED on the board is connected to PG0 to PG2. If you access the device file with the number from 192 to 194 corresponding to this number, the full-color LED will light up. However, since this LED anode is common, it turns on at 0 and turns off at 1. In the first place, the port becomes 0 by initialization, so it lights up after initialization.

# 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

If you want to read the value of the switch connected to PG3, you can do the following and 0 or 1 will be returned. (The switch requires a pull-up resistor.)

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

The correspondence between the port number and the device file number is written in the official document, so you can check it. http://zero.lichee.pro/%E9%A9%B1%E5%8A%A8/GPIO_file.html

Method 2 Use memory access

As far as I read the official documentation, it says that there is a library that can operate GPIO in this way, but I could not find it anywhere. Probably not because the community has come to the conclusion that there was no similar question a few years ago. So I made it because I wanted it, but it was quite difficult because it was the first time. I don't have much information ... The only functions are port initialization / setting and input / output. Interrupts are not implemented. Since the amount is small, I wrote everything in the header file.

test_io.h


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

/*Physical address of peripheral register(From the BCM2835 specifications) */
#define REG_ADDR_BASE        (0x01C20800)       /* bcm_host_get_peripheral_address()Is better*/
#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;    /*Virtual address to GPIO register(User space) */
int fd;


int io_init(void){
    /*Open device file for memory access*/
    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        perror("open");
        return -1;
    }

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

    /* ARM(CPU)Physical address seen from → Mapping to virtual address*/
    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){
    /*Release used resources*/
    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);
}

A sample program in which blue and green flash alternately.

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

Compile / execute

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

Sample program that lights up when the switch connected to PG3 is pressed (The switch requires a pull-up resistor.)

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

Compile / execute

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

Also, other than this program PG, I have not actually tested by connecting parts yet, so it may not work well. I have confirmed to some extent whether the register status has changed, but ...

bonus

This program is a status list display program for port-related registers.

pin_status.c


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

/*Physical address of peripheral register(From the BCM2835 specifications) */
#define REG_ADDR_BASE        (0x01C20800)       /* bcm_host_get_peripheral_address()Is better*/
#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;    /*Virtual address to GPIO register(User space) */
int fd;


int io_init(void){
    /*Open device file for memory access*/
    if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
        perror("open");
        return -1;
    }

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

    /* ARM(CPU)Physical address seen from → Mapping to virtual address*/
    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){
    /*Release used resources*/
    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:Invalid", j);
            }
            else if(port_status[i][j] == 0){
                printf("%02d:input", j);
            }
            else if(port_status[i][j] == 1){
                printf("%02d:output", j);
            }
            else{
                printf("%02d:other%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();
}
    

Reference material

Official document: GPIO [Main chip data sheet] (https://linux-sunxi.org/images/2/23/Allwinner_V3s_Datasheet_V1.0.pdf) [Sipeed Lichee Zero schematic] (https://dl.sipeed.com/LICHEE/Zero/HDK/lichee_zero.pdf) How to make an embedded Linux device driver (5)

Recommended Posts

L Chika with Sipeed Lichee Zero (GPIO operation)
Remote L Chika with pigpio
Try L Chika with raspberry pi
Connect Sipeed Lichee Zero to the net
RaspberryPi L Chika with Python and C #
[C, C ++, Python, JavaScript] L Chika with Edison