duo256m的多种GPIO使用方式及代码

摘要

关于GPIO的使用,本文给出了多种操作方式,包括使用终端控制、系统IO(open、write等)控制、寄存器控制、内核态控制、cdev字符设备驱动控制、以及misc杂项设备驱动等,并且给出了简洁可用的代码参考。此外,本文还简要说明了工具链的安装、编译流程、nfs配置等。
另外,本文提到的led,您可能需要自己用面包板搭建电路,这很简单,只需要在想要控制的GPIO引脚与地(GND)之间连接一个led灯即可,请注意极性方向。并且,以下代码均在sipeed LicheeRV Nano上运行成功。

一、前期准备
1.1 设置nfs
参考链接
nfs可以实现电脑端与duo文件互传。

#以下命令在电脑端执行
ifconfig										#查看电脑端IP,很重要,请记住,在下方会用到
sudo apt-get install nfs-kernel-server    		#安装nfs-kernel-server
sudo apt-get install gedit						#安装gedit编辑器
sudo mkdir /home/cunjiang/nfs					#创建电脑端nfs文件夹,该文件夹位置可以自行设置
sudo gedit /etc/exports							#打开配置文件
/home/cunjiang/nfs *(rw,sync,no_root_squash)	#在文件末尾添加
# 在/etc/exports文件末尾添加
/home/cunjiang/nfs *(rw,sync,no_root_squash)	
/etc/init.d/nfs-kernel-server restart			#重启 nfs 服务
#以下命令在duo端执行
mkdir /mnt/nfs/												  #创建duo端nfs文件夹,该文件夹位置可以自行设置
mount -t nfs -o nolock 电脑端IP:/home/cunjiang/nfs /mnt/nfs/	#挂载nfs
ifconfig												      #查看duo端IP,很重要,请记住,在下方会用到

1.2 安装编译工具链
参考链接

#以下命令在电脑端执行
sudo apt-get install wget git make						#安装依赖工具
git clone https://github.com/milkv-duo/duo-examples.git	#获取工具链
cd duo-examples											#进入文件夹
source envsetup.sh										#加载编译环境

/duo-examples/duo-sdk/riscv64-linux-musl-x86_64/bin/路径下即为编译所需的工具链,通常使用的是riscv64-unknown-linux-musl-gcc,请在该文件所在文件夹右键打开终端并使用命令pwd查看该gcc编译器路径,等会儿会用到。

工具链的编译测试请自行参考上方参考链接中的说明哦。
1.3 预编译
参考链接
依次在电脑终端执行以下命令:

export ARCH=riscv

export CROSS_COMPILE=/home/cunjiang/milkv/duo-examples/duo-sdk/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-

cp /home/cunjiang/milkv/duo-buildroot-sdk/build/boards/cv181x/cv1812cp_milkv_duo256m_sd/linux/cvitek_cv1812cp_milkv_duo256m_sd_defconfig /home/cunjiang/milkv/duo-buildroot-sdk/linux_5.10/arch/riscv/configs/cvitek_cv1812cp_milkv_duo256m_sd_defconfig

make -C /home/cunjiang/milkv/duo_256m/duo-buildroot-sdk/linux_5.10/ cvitek_cv1812cp_milkv_duo256m_sd_defconfig

make -C /home/cunjiang/milkv/duo_256m/duo-buildroot-sdk/linux_5.10

验证

创建helloworld.c文件,并写入以下代码:

//helloworld.c

#include "linux/init.h"
#include "linux/module.h"

static int __init helloworld_init(void) {
    printk("helloworld\n");
    return 0;
}

static void __exit helloworld_exit(void) {
    printk("goodbye\n");
}

MODULE_LICENSE("GPL");
module_init(helloworld_init);
module_exit(helloworld_exit);

创建Makefile文件,注意:kdir 请根据自己的实际情况修改路径

# Makefile

export ARCH=riscv
export CROSS_COMPILE=riscv64-unknown-linux-musl-
obj-m +=helloworld.o
kdir=/home/cunjiang/milkv/duo_256m/duo-buildroot-sdk/linux_5.10
pwd=$(shell pwd)
all:
	make -C $(kdir) M=$(pwd) modules

将以上两个文件放入同一文件夹,使用make 命令编译,然后将编译出来的helloworld.ko 文件传到板子上,使用insmod helloworld.ko 加载模块,使用rmmod helloworld.ko 卸载模块,最后使用dmesg 查看。


1.4 命令alias(可选)
该部分是可选的,只是为了使用方便一点。
Ctrl + Alt + T 打开终端,输入:sudo gedit .bashrc
在打开的文件末尾补充以下:

duo端IPgcc编译器路径请查看上方教程。

duo端IP示例:192.168.123.456 补充:若板子无网口,则duo端IP为192.168.42.1

gcc编译器路径示例:/home/cunjiang/milkv/duo-examples/duo-sdk/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc

alias duo256m_login='ssh root@duo端IP'
alias duo256m_gcc='gcc编译器路径/riscv64-unknown-linux-musl-gcc'
function duo256m_scp(){
	scp $1 root@duo端IP:/mnt/nfs
}

保存并关闭文件,然后在终端输入:

source .bashrc

(1) duo256m_login用于通过ssh登陆duo;
(2) duo256m_gcc用于编译c文件,例如:

duo256m_gcc  app.c -o app

(3)duo256m_scp用于传输文件,例如:

duo256m_scp app

2 控制LED亮灭
2.1 命令控制与C程序

ls /sys/class/gpio/
echo 494 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio494/direction
echo 1 > /sys/class/gpio/gpio494/value
echo 0 > /sys/class/gpio/gpio494/value

GPIO表格

GPIO name	GPIO pin	GPIO number	Notes
GPIOA14        19        494        
GPIOA15        20        495        
GPIOA16        16        496        
GPIOA17        17        497        
GPIOA22        24        502        
GPIOA23        21        503        
GPIOA24        22        504        
GPIOA25        25        505        
GPIOA26        27        506        
GPIOA27        26        507        
GPIOA28        1         508         
GPIOA29        2         509         

image

/// led.c



#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
    int fd=open("/sys/class/gpio/export",O_WRONLY);
    if(fd==-1) {
        printf("export open error\n");
        return -1;
    }
    write(fd,"494",sizeof("494"));
    close(fd);

    fd=open("/sys/class/gpio/gpio494/direction",O_RDWR);
    if(fd==-1) {
        printf("direction open error\n");
        return -1;
    }
    write(fd,"out",sizeof("out"));
    close(fd);

    fd=open("/sys/class/gpio/gpio494/value",O_RDWR);
    if(fd==-1) {
        printf("value open error\n");
        return -1;
    }


    int i=20;
    while (i--) {
        i%2==0?write(fd,"1",sizeof("1")):write(fd,"0",sizeof("0"));
        sleep(1);
    }


    close(fd);


    return 0;
}
# Makefile




cc=/home/cunjiang/milkv/duo-examples/duo-sdk/riscv64-linux-musl-x86_64/bin/riscv64-unknown-linux-musl-gcc
var=led.o

led:$(var)
	$(cc) $^ -o led -static

%.o:%.c
	$(cc) -c $< -o $@ -static

clean:
	rm -rf *.o

2.2 驱动控制
简易代码

// led_kernel.c

#include "linux/init.h"
#include "linux/module.h"
#include "linux/gpio.h"


int gpio_num=494;

static int __init led_init(void) {
    printk("led_init\n");
    gpio_request(gpio_num,NULL);
    gpio_direction_output(gpio_num,0);
    gpio_set_value(gpio_num,1);
    return 0;
}

static void __exit led_exit(void) {
    printk("led_exit\n");
    gpio_free(gpio_num);
}

MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);
# Makefile
export ARCH=riscv
export CROSS_COMPILE=riscv64-unknown-linux-musl-
obj-m +=led_kernel.o
kdir=/home/cunjiang/milkv/duo_256m/linux_sdk/duo-buildroot-sdk/linux_5.10
all:
	make -C $(kdir) M=$(shell pwd) modules

clean:
	rm -rf *.o *.ko

###misc代码
参考链接

//led_kernel.c

#include "linux/init.h"
#include "linux/module.h"
#include "linux/gpio.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/uaccess.h"

int gpio_num=494;

ssize_t led_write(struct file *file, const char __user *user_buf, size_t size, loff_t * loff){
    char kernel_buf[64]={0};
    printk("led_write\n");
    if(copy_from_user(kernel_buf,user_buf,size)!=0) {
        printk("copy_from_user error\n");
        return -1;
    }else {
        if (kernel_buf[0]=='1') {
            gpio_set_value(gpio_num,1);
        }else if(kernel_buf[0]=='0') {
            gpio_set_value(gpio_num,0);
        }
        return 0;
    }
}

int led_open(struct inode *inode, struct file *file){
    printk("led_open\n");
    gpio_request(gpio_num,NULL);
    gpio_direction_output(gpio_num,0);
    return 0;
}

int led_release(struct inode *inode, struct file *file){
    printk("led_release\n");
    gpio_free(gpio_num);
    return 0;
}

static struct file_operations led_fops={
    .owner = THIS_MODULE,
    .write = led_write,
    .open = led_open,
    .release = led_release
};

static struct miscdevice led_dev={
    .minor = MISC_DYNAMIC_MINOR,
    .fops = &led_fops,
    .name = "led_dev"
};


static int __init led_init(void) {
    printk("led_init\n");
    if(misc_register(&led_dev)<0) {
        printk("misc_register error\n");
        return -1;
    }else {
        printk("misc_register succeed\n");
        return 0;
    }
}

static void __exit led_exit(void) {
    printk("led_exit\n");
    misc_deregister(&led_dev);
}

MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);
// app.c
//
// Created by cunjiang on 24-2-28.
//
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char *argv[]) {
    int fd=open("/dev/led_dev",O_RDWR);
    if(fd<0) {
        perror("open error");
        return -1;
    }else {
        write(fd,argv[1],sizeof(argv[1]));
        close(fd);
        return 0;
    }
}
# app Makefile
cc=riscv64-unknown-linux-musl-gcc
var=app.o
app:$(var)
	$(cc) $^ -o app -static

%.o:%.c
	$(cc) -c $< -o $@ -static

clean:
	rm -rf *.o

2.3 操作寄存器控制
该部分通过寄存器方式控制led,仅led_kernel.c 作了更改,其余文件不变。
参考链接
在SG2002的寄存器(TRM)手册,可以看到具体操作顺序:先配置DDR寄存器设置GPIO作为输入还是输出,再配置DR寄存器设置高低电平。

image
image

//led_kernel.c

#include "linux/init.h"
#include "linux/module.h"
#include "linux/gpio.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/uaccess.h"
#include "linux/io.h"

#define GPIO494_DDR (0x03020000+0x000)
#define GPIO494_DR (0x03020000+0x004)
unsigned int *vir_gpio494_ddr;
unsigned int *vir_gpio494_dr;


ssize_t led_write(struct file *file, const char __user *user_buf, size_t size, loff_t * loff){
    char kernel_buf[64]={0};
    printk("led_write\n");
    if(copy_from_user(kernel_buf,user_buf,size)!=0) {
        printk("copy_from_user error\n");
        return -1;
    }else {
        if (kernel_buf[0]=='1') {
            //gpio_set_value(gpio_num,1);
            *vir_gpio494_dr|=(1<<14);
        }else if(kernel_buf[0]=='0') {
            //gpio_set_value(gpio_num,0);
            *vir_gpio494_dr&=~(1<<14);
        }
        return 0;
    }
}

int led_open(struct inode *inode, struct file *file){
    printk("led_open\n");
    // gpio_request(gpio_num,NULL);
    // gpio_direction_output(gpio_num,0);
    return 0;
}

int led_release(struct inode *inode, struct file *file){
    printk("led_release\n");
    //gpio_free(gpio_num);
    return 0;
}

static struct file_operations led_fops={
    .owner = THIS_MODULE,
    .write = led_write,
    .open = led_open,
    .release = led_release
};

static struct miscdevice led_dev={
    .minor = MISC_DYNAMIC_MINOR,
    .fops = &led_fops,
    .name = "led_dev"
};


static int __init led_init(void) {
    printk("led_init\n");
    if(misc_register(&led_dev)<0) {
        printk("misc_register error\n");
        return -1;
    }else {
        printk("misc_register succeed\n");

        vir_gpio494_ddr=ioremap(GPIO494_DDR,4);
        vir_gpio494_dr=ioremap(GPIO494_DR,4);
        if (vir_gpio494_ddr==NULL ||vir_gpio494_dr==NULL) {
            printk("ioremap error\n");
            return -1;
        }else {
            printk("ioremap succeed\n");
        }
        *vir_gpio494_ddr|=(1<<14);
        *vir_gpio494_dr|=(1<<14);
        return 0;
    }
}

static void __exit led_exit(void) {
    printk("led_exit\n");
    misc_deregister(&led_dev);
    iounmap(vir_gpio494_ddr);
    iounmap(vir_gpio494_dr);
}

MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);

image
2.4 字符设备cdev代码

#include "linux/init.h"
#include "linux/module.h"
#include "linux/gpio.h"
#include "linux/miscdevice.h"
#include "linux/fs.h"
#include "linux/uaccess.h"
#include "linux/io.h"
#include "linux/cdev.h"
#include "linux/device.h"

#define GPIO494_DDR (0x03020000+0x000)
#define GPIO494_DR (0x03020000+0x004)
unsigned int *vir_gpio494_ddr;
unsigned int *vir_gpio494_dr;

dev_t dev_id;
struct class *class;
struct device *device;
struct cdev cdev={
    .owner = THIS_MODULE
};



ssize_t led_write(struct file *file, const char __user *user_buf, size_t size, loff_t * loff){
    char kernel_buf[64]={0};
    printk("led_write\n");
    if(copy_from_user(kernel_buf,user_buf,size)!=0) {
        printk("copy_from_user error\n");
        return -1;
    }else {
        if (kernel_buf[0]=='1') {
            //gpio_set_value(gpio_num,1);
            *vir_gpio494_dr|=(1<<14);
        }else if(kernel_buf[0]=='0') {
            //gpio_set_value(gpio_num,0);
            *vir_gpio494_dr&=~(1<<14);
        }
        return 0;
    }
}

int led_open(struct inode *inode, struct file *file){
    printk("led_open\n");
    // gpio_request(gpio_num,NULL);
    // gpio_direction_output(gpio_num,0);
    vir_gpio494_ddr=ioremap(GPIO494_DDR,4);
    vir_gpio494_dr=ioremap(GPIO494_DR,4);
    if (vir_gpio494_ddr==NULL ||vir_gpio494_dr==NULL) {
        printk("ioremap error\n");
        return -1;
    }else {
        printk("ioremap succeed\n");
    }
    *vir_gpio494_ddr|=(1<<14);
    *vir_gpio494_dr|=(1<<14);
    return 0;
}

int led_release(struct inode *inode, struct file *file){
    printk("led_release\n");
    //gpio_free(gpio_num);
    return 0;
}

static struct file_operations led_fops={
    .owner = THIS_MODULE,
    .write = led_write,
    .open = led_open,
    .release = led_release
};


static int __init led_init(void) {
    printk("led_init\n");
    if(alloc_chrdev_region(&dev_id,0,1,"led_chr_device")<0) {
        printk("alloc_chrdev_region error\n");
        return -1;
    }else {
        printk("alloc_chrdev_region succeed\n");
        printk("major_id: %d, minor_id: %d\n", MAJOR(dev_id), MINOR(dev_id));
        cdev_init(&cdev,&led_fops);
        cdev_add(&cdev,dev_id,1);
        class=class_create(THIS_MODULE,"led_chr_class");
        device=device_create(class,NULL,dev_id,NULL,"led_dev");
        return 0;
    }
}

static void __exit led_exit(void) {
    printk("led_exit\n");
    cdev_del(&cdev);
    device_destroy(class,dev_id);
    class_destroy(class);
    iounmap(vir_gpio494_ddr);
    iounmap(vir_gpio494_dr);
}

MODULE_LICENSE("GPL");
module_init(led_init);
module_exit(led_exit);

image

image
image