Linux-ARM架构,全志H616香橙派开发笔记

Linux-ARM架构,全志H616香橙派开发笔记_第1张图片

Linux-ARM架构,全志H616香橙派开发笔记_第2张图片

一、前期准备工作

1、材料准备

①SD卡及读卡器
②CH340USB转232串口
③全志H616香橙派和type-C转USB供电线
④ PC一台
⑤SD卡格式化软件SD Card Formatter、镜像写入软件Win32DiskImager、
开发工具MobaXterm_Personal_20.3.exe

2、烧录工作

①SD卡格式化
Linux-ARM架构,全志H616香橙派开发笔记_第3张图片

②系统镜像写入
Linux-ARM架构,全志H616香橙派开发笔记_第4张图片

 ③开发工具MobaXterm_Personal_20.3.exe
Linux-ARM架构,全志H616香橙派开发笔记_第5张图片



 

二、全志H616香橙派配置

1、密码修改 

默认账户密码如下图

为了登录简便,我们将密码改为1,使用sudo passwd orangepi指令
其中sudo是超级用户权限 passwd是修改密码指令 orangepi是账户

Linux-ARM架构,全志H616香橙派开发笔记_第6张图片

2、连入无线网 

①、命令扫描周围的WIFI热点 nmcli dev wifi
Linux-ARM架构,全志H616香橙派开发笔记_第7张图片

②、命令接入网络 nmcli dev wifi connect iLovePency password PKF116116

③、ifconfig指令查看接入无线网IP地址
ip addr show wlan0指令也可以查看
Linux-ARM架构,全志H616香橙派开发笔记_第8张图片

④、SSH方式登录开发板
这是企业开发调试必用方式,比串口来说不用接线,前提是接入网络并获得板子IP地址,且系统做了 SSH的服务器,本镜像自带SSH服务器,所以通过mobaXterm登陆就行
这种网络访问的方式可以多窗口
Linux-ARM架构,全志H616香橙派开发笔记_第9张图片
Linux-ARM架构,全志H616香橙派开发笔记_第10张图片

⑤、修改uboot内核日志输出的权限,方便后期看到内核输出的信息
使用超级用户权限进入/boot/orangepiEnv.txt文件     sodu vi /boot/orangepiEnv.txt  

Linux-ARM架构,全志H616香橙派开发笔记_第11张图片
Linux-ARM架构,全志H616香橙派开发笔记_第12张图片

⑥、设置tab缩进

sudo vim /etc/vim/vimrc

set tabstop=4 设置tab键缩进4个空格

set shiftwidth=4 设置批量对齐时候的tab键空格数为4

Linux-ARM架构,全志H616香橙派开发笔记_第13张图片

Linux-ARM架构,全志H616香橙派开发笔记_第14张图片

3、通过网口eth0连入香橙派

方法博文

三、基于官方外设开发

1、wiringPi外设SDK安装 

git clone https://github.com/orangepi-xunlong/wiringOP //下载源码
cd wiringOP //进入文件夹
sudo ./build clean //清除编译信息
sudo ./build //编译

通过windows浏览器打开https://github.com/orangepi-xunlong/wiringOP
下载压缩包
把压缩包通过xterm传到开发板
解压 unzip xxx.zip
cd xxx
sudo ./build

验证指令:gpio readall

 如下图:gpio readall可以回复入下图所示证明外设库已经安装好了

Linux-ARM架构,全志H616香橙派开发笔记_第15张图片

2、老演员蜂鸣器 

#include 
#include 
#include 

#define Beep 0   //26pin

int main (void)
{
    wiringPiSetup();

    pinMode(Beep, OUTPUT);

    while(1) {
        digitalWrite (Beep, HIGH) ;      // On
        printf("1\n");
        sleep(2);
        digitalWrite (Beep, LOW) ;       // Off
        printf("0\n");
        sleep(2);
    }

    return 0;
}

 3、小插曲 shell脚本

./build beep.c
 $0         $1
shell脚本处理参数,可以通过$?来处理,这里的$1是要编译的文件 
简易编译的shell脚本: gcc $1 -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt

给shell脚本添加exe可执行权限

chmod +x build.sh

4、时间函数 

#include

int gettimeofday(struct timeval *tv,struct timezone *tz )

gettimeofday()会把目前的时间用tv 结构体返回,当地时区的信息则放到tz所指的结构中
struct timeval
{ long tv_sec;/*秒*/
long tv_usec;/*微妙*/
};

#include 
#include 


//#include
//int gettimeofday(struct timeval *tv,struct timezone *tz )
//struct timeval
//{
//long tv_sec;/*秒*/
//long tv_usec;/*微妙*/
//};


void cnttime()
{
    int i,j;
    for(i;i<100;i++)
        for(j;j<10000;j++);


}

int main()
{
    struct timeval timestart;
    struct timeval timestop;
    gettimeofday(×tart,NULL);
    cnttime();
    gettimeofday(×top,NULL);
    long timediff = (timestop.tv_sec-timestart.tv_sec)*1000000+(timestop.tv_usec-timestart.tv_usec);
    printf("H616 count 100W time:%ld\n",timediff);

    return 0;
}

5、超声波测距代码

#include 
#include 
#include 
#include 
#include 

#define Trig 0
#define Echo 1

double getdistance()
{
    double dis;
    struct timeval start;
    struct timeval stop;

    digitalWrite(Trig,LOW);
    usleep(5);
    digitalWrite(Trig,HIGH);
    usleep(10);
    digitalWrite(Trig,LOW);

    while(!digitalRead(Echo));
    gettimeofday(&start,NULL);
    while(digitalRead(Echo));
    gettimeofday(&stop,NULL);

    long diffTime =1000000*(stop.tv_sec-start.tv_sec)+(stop.tv_usec-start.tv_usec);

    dis = (double)diffTime/1000000 *34000/2;

    return dis;


}

int main()
{
    double dis;
    if(wiringPiSetup() != 0 ){
        printf("wiringPiSetup error!\n");
    }

    pinMode(Trig,OUTPUT);
    pinMode(Echo,INPUT);


    while(1){
        dis = getdistance();

        printf("distance is %lf cm\n",dis);

        sleep(1);
    }

    return 0;
}

6、Linux定时器

API

#include

int setitimer (__itimer_which_t __which, const struct itimerval *__restrict __new, struct itimerval *__restrict __old);

函数作用:设定定时器初值及延时多久时间去执行定时器计时

__which:三种类型
ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。 ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。 ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。

__restrict __new:
struct itimerval {
/* Value to put into `it_value' when the timer expires. */
struct timeval it_interval;
/* Time to the next timer expiration. */
struct timeval it_value;
};
it_interval:定时器初始值
it_value:当定时器被设置OK,延时多久时间去执行定时器计时 //这个值不能设置为0!!

struct timeval {
__time_t tv_sec; /* Seconds. */
__suseconds_t tv_usec; /* Microseconds. */
};

__restrict __old
可以不设置,用NULL

返回值: 成功执行时,返回0。失败返回-1

很明显,这边需要捕获对应的信号进行逻辑相关处理 signal(SIGALRM,signal_handler);

一个进程只能创建一个定时器!!!

定时器使用案例

#include 
#include 
#include 
#include 



static int i = 0;
void signal_handler()
{

    i++;
    if(i == 2000){
        printf("hellow\n");
        i = 0;
    }

}

int main()
{
    struct itimerval itv;

    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 500;

    itv.it_value.tv_sec = 0;
    itv.it_value.tv_usec = 100;

    if((setitimer(ITIMER_REAL,&itv,NULL)) == -1){
        perror("setitimer");
        exit(-1);
    }

    signal(SIGALRM,signal_handler);

    while(1);


    return 0;
}

基于定时器控制sg90

#include 
#include 
#include 
#include 
#include 

#define SG90 0




static int i = 0;
int angle;

void signal_handler()
{

    if(i == 40){
        i = 0;
    }
    if(i < angle){
        digitalWrite(SG90,HIGH);
    }else{
        digitalWrite(SG90,LOW);
    }
    i++;

}

int main()
{
    struct itimerval itv;
    angle = 0;
    wiringPiSetup();
    pinMode(SG90,OUTPUT);


    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 500;

    itv.it_value.tv_sec = 0;
    itv.it_value.tv_usec = 100;

    if((setitimer(ITIMER_REAL,&itv,NULL)) == -1){
        perror("setitimer");
        exit(-1);
    }

    signal(SIGALRM,signal_handler);

    while(1){
        printf("please input angle     ps:1-0 2-45 3-90 4-135 5-180\n ");
        scanf("%d",&angle);
    }


    return 0;
}

四、I2C协议应用

1、前提准备 

1、启动Linux系统后,先确认/dev下存在i2c-3的设备节点

Linux-ARM架构,全志H616香橙派开发笔记_第16张图片2、安装i2c-tools          指令:sudo apt-get install i2c-tools 

3、检测当前i2c设备     指令:sudo i2cdetect -y 3

oled_demo.c

#include 
#include 
#include 
#include 
#include 
#include 

#include "oled.h"
#include "font.h"

int oled_show(struct display_info *disp) {
    int i;
    char buf[100];

    disp->font = font1;
    oled_putstrto(disp, 0, 10, "Welcome to my home");
    disp->font = font1;
    oled_putstrto(disp, 0, 20, "Kevin handsome");
    oled_send_buffer(disp);

    return 0;
}


void show_usage(char *progname) {
    printf("\nUsage:\n%s \n", progname);
}

int main(int argc, char **argv) {
    int e;
    char filename[32];
    struct display_info disp;

    if (argc < 2) {
        show_usage(argv[0]);

        return -1;
    }

    memset(&disp, 0, sizeof(disp));
    sprintf(filename, "%s", argv[1]);
    disp.address = OLED_I2C_ADDR;

    e = oled_open(&disp, filename);
    e = oled_init(&disp);
    oled_show(&disp);

    return 0;
}

五、串口通讯

1、wiringPi库实现串口通讯

serial.c

#include 
#include 
#include 

#include 
#include 
#include 
#include 

int fd;

void *sendHandle()
{
    char *sendBuf;
    sendBuf = (char *)malloc(128*sizeof(char));
    while(1){
        memset(sendBuf,'\0',128);
        scanf("%s",sendBuf);
        while(*sendBuf !='\0'){
            serialPutchar(fd,*sendBuf++);
        }
    }
}

int main ()
{
    pthread_t send;

    if ((fd = serialOpen ("/dev/ttyS5", 115200)) < 0)
    {
        fprintf (stderr, "Unable to open serial device: %s\n", strerror (errno)) ;
        return 1 ;
    }

    if (wiringPiSetup () == -1)
    {
        fprintf (stdout, "Unable to start wiringPi: %s\n", strerror (errno)) ;
        return 1 ;
    }

    pthread_create(&send,NULL,sendHandle,NULL);


    while(1){
        while (serialDataAvail (fd))
        {
            printf ("%c", serialGetchar(fd));
            fflush (stdout);
        }
    }

    return 0 ;
}

2、不使用wiringPi库实现串口通讯

serialtools.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

int myserialOpen (const char *device, const int baud)
{
    struct termios options ;
    speed_t myBaud ;
    int status, fd ;
    switch (baud)
    {
        case    9600:   myBaud =    B9600 ; break ;
        case  115200:   myBaud =  B115200 ; break ;
        default:
                        return -2 ;
    }

    if ((fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK)) == -1)
        return -1 ;
    fcntl (fd, F_SETFL, O_RDWR) ;

    // Get and modify current options:

    tcgetattr (fd, &options) ;

    cfmakeraw   (&options) ;
    cfsetispeed (&options, myBaud) ;
    cfsetospeed (&options, myBaud) ;

    options.c_cflag |= (CLOCAL | CREAD) ;
    options.c_cflag &= ~PARENB ;
    options.c_cflag &= ~CSTOPB ;
    options.c_cflag &= ~CSIZE ;
    options.c_cflag |= CS8 ;
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG) ;
    options.c_oflag &= ~OPOST ;

    options.c_cc [VMIN]  =   0 ;
    options.c_cc [VTIME] = 100 ;    // Ten seconds (100 deciseconds)

    tcsetattr (fd, TCSANOW, &options) ;

    ioctl (fd, TIOCMGET, &status);

    status |= TIOCM_DTR ;
    status |= TIOCM_RTS ;

    ioctl (fd, TIOCMSET, &status);

    usleep (10000) ;    // 10mS

    return fd ;

}

int serialSendString (const int fd, const char *s)
{
    int ret;
    ret = write (fd, s, strlen (s));
    if (ret < 0)
        printf("Serial Puts Error\n");
    return ret;
}

int serialRcvString (const int fd,char *buf)
{
    int ret;

    if((ret = read (fd, buf, 128)) < 0)
        return -1 ;

    return ret;
}

serialtools.h

int myserialOpen (const char *device, const int baud);
int serialSendString (const int fd, const char *s);
int serialRcvString (const int fd,char *buf);

serial.c

#include 
#include 
#include 
#include 

#include "serialtools.h"


int fd;

void *sendHandle()
{
    char buf[128] ={0};
    while(1){
        memset(buf,'\0',sizeof(buf));
        printf("->");
        scanf("%s",buf);
        serialSendString(fd,buf);
    }
}

void *receiveHandle()
{
    char buf[128] ={0};
    while(1){
        memset(buf,'\0',sizeof(buf));
        serialRcvString(fd,buf);
        printf("%s\n",buf);
    }

}

int main(int argc,char **argv)
{
    if(argc != 2){
        printf("arg num is error!\n");
        return -1;
    }
    pthread_t send_t;
    pthread_t receive_t;

    if((fd = myserialOpen (argv[1], 115200)) < 0){
        printf("serial open error!\n");
        return -2;
    }

    pthread_create(&send_t,NULL,sendHandle,NULL);
    pthread_create(&receive_t,NULL,receiveHandle,NULL);


    while(1){
        sleep(1);
    }
    return 0;
}

五、语音模块控制安卓手机刷抖音

指令含义

1、dmesg        查看外接设备信息(比如说USB口接的设备信息)

2、sudo apt-get install adb        安装adb的工具

3、adb devices        列出所有的adb设备

a. 把手机接入开发板
 

b. 安装adb工具,在终端输入adb安装指令: sudo apt-get install adb
 

c. demesg能查看到手机接入的信息,但是输入adb devices会出现提醒 dinsufficient permissions for device: user in plugdev group; are your udev rules wrong?
 

d. 配置文件,以支持USB设备的热拔插,支持UDEV的机制 在/etc/udev/rules.d 文件夹下创建规则文件 cd /etc/udev/rules.d/ sudo vim 51-android.rules 在文件中添加内容 SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"


e. 在手机开发者选项中,打开USB调试,重新拔插手机 f. 手机弹出调试提醒,点确认手机调试模式

db shell input swipe 540 1300 540 500 100 向下滑动540是水平的,1300是竖直方向,下 是 500


adb shell input swipe 540 500 540 1300 100 向上滑动


adb shell "seq 3 | while read i;do input tap 350 1050 & input tap 350 1050 & sleep 0.01;done;" 点赞


adb shell input keyevent 26 锁屏

六、UDEV

1、Linux的热拔插UDEV机制

 udev是一个设备管理工具,udev以守护进程的形式运行,通过侦听内核发出来的uevent(事件)来管理/dev目录下的设备文件。udev在用户空间下运行,而不在内核空间运行。它能根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除等。设备文件通常放在/dev目录下,使用udev后,使用udev后,在/dev目录下就只包含系统中真正存在的设备。

2、守护进程 

Linux Daemon(守护进程)是运行在后台的一种特殊进程,它独立于控制终端并且周期性的执行某些任务或者等待处理某些发生的事件。它不需要用户输入就能运行并且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd,web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。守护进程的名称通常以d结尾

守护进程基本特点

1、生存周期长【非必须】,一般操作系统启动的时候就启动,关闭的时候就关闭,但并非说你的进程是守护进程,操作系统就知道,然后在操作系统启动时启动它,需要人为的开发工程师在系统启动的时候在开机脚本里去添加脚本去启动这个进程

2、守护进程和终端无关联,所以当控制终端退出,守护进程也不会退出

3、守护进程是在后台进行的,不会占用终端,终端可以执行其他命令

4、一个守护进程的父进程是init进程,原因是它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程

可以通过指令 ps -elf查看所有进程

使用ps -elf |grep udev对进程进行检索

使用ps -elf |grep udev |grep -v grep略过grep进程的检索

ppid = 0:内核进程,跟随系统启动而启动,生命周期贯穿整个系统。

cmd列名带[]这种,叫内核守护进程

老祖init:也是系统守护进程,它负责启动各运行层次特定的系统服务;所以很多进程的PPID是init,也负责收养孤儿进程。 cmd列中名字不带[]的普通守护进程(用户集守护进程)

Linux-ARM架构,全志H616香橙派开发笔记_第17张图片

3、自己做一个守护进程

创建守护进程API
#include


int daemon(int nochdir,int noclose);
函数作用
:创建守护进程

nochdir:赋值为0表示将守护进程的工作目录改为“/”根目录

noclose:赋值为0表示将标准的输入、输出和错误输出重定向到/dev/null

返回值:成功返回0,失败返回-1

时间函数API
#include


time_t time(time_t *seconds);
函数作用:计算当前日历时间,并把它编码成 time_t 格式

seconds:如果该参数不为NULL,则返回值也储存到该参数中

返回值:返回当前日历时间(time_t 格式)

struct tm *localtime(const time_t *timer);
函数作用
:将日历时间(time_t格式)的指针转换为tm结构体指针

timer:time_t格式日历时间指针

返回值:tm结构体指针

struct tm {
   int tm_sec;         /* 秒,范围从 0 到 59        */
   int tm_min;         /* 分,范围从 0 到 59        */
   int tm_hour;        /* 小时,范围从 0 到 23        */
   int tm_mday;        /* 一月中的第几天,范围从 1 到 31    */
   int tm_mon;         /* 月,范围从 0 到 11        */
   int tm_year;        /* 自 1900 年起的年数        */
   int tm_wday;        /* 一周中的第几天,范围从 0 到 6    */
   int tm_yday;        /* 一年中的第几天,范围从 0 到 365    */
   int tm_isdst;       /* 夏令时                */
};


char *asctime(const tm *timeptr);

函数作用:将tm结构体指针转换为字符串(人看得懂的日历)指针

timeptr:tm结构体指针

返回值:字符串指针(人看得懂的日历)

timedaemon.c 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

bool flags = true;

void handle(int signum,siginfo_t *infomation,void *context)
{
    printf("get signal %d,i am quit\n",signum);
    flags = false;
}

int main()
{
    int fd;
    struct sigaction act;
    time_t t;
    char *buf;

    if(daemon(0,0) == -1){
        printf("daemon error!\n");
        exit(-1);
    }

    act.sa_sigaction = handle;
    act.sa_flags = SA_SIGINFO;
    if(sigaction(SIGQUIT,&act,NULL) == -1){
        printf("sigaction error!\n");
        exit(-2);
    }

    while(flags){
        if((fd = open("/home/orangepi/tdaemon.log",O_RDWR|O_CREAT|O_APPEND,0666)) == -1){
            printf("open error!\n");
            exit(-3);
        }

        t = time(NULL);
        buf =asctime(localtime(&t));

        write(fd,buf,strlen(buf));

        close(fd);

        sleep(10);
    }


    return 0;
}

编译完.c文件后运行程序,发现终端并不会被程序所占用,并执行ps -elf |grep tdaemon指令后发现进程已经在系统中运行着,此时在/home/orangepi/路径下发现tdaemon.log文件已经被创建,进入文件发现数据正是想要的数据,证明守护进程已经完美的跑起来了

Linux-ARM架构,全志H616香橙派开发笔记_第18张图片

 此时需要设置开机自动启动该守护进程,首先要获取到该守护进程执行文件的路径(包含文件名),将该路径复制到/etc/rc.local文件中即可开机自启动,进入文件指令为sudo vi /etc/rc.local

Linux-ARM架构,全志H616香橙派开发笔记_第19张图片

4、做一个程序来判断一个进程是否存在

test.c

这里先做一个demo程序,执行起来不退出,用于检测程序的测试

#include 
#include 

int main()
{
    int i = 0;
    while(1){
        printf("%d\n",i++);

        sleep(1);
    }
    return 0;
}

judge.c

这里用到popen函数执行ps指令,再通过其返回值与想要检测进程的名称使用strstr()函数进行判断系统中是否运行了我们想要的进程

#include 
#include 
#include 

int main()
{
    while(1){
        char buf[128]={'\0'};
        FILE *file;
        file = popen("ps -elf |grep test |grep -v grep","r");

        fread(buf,1,128,file);

        if(strstr(buf,"test") != NULL){
            printf("process is existed!\n");
        }else{
            printf("process is not existed!\n");
        }
        printf("buf is %s\n",*buf=='\0'?"NULL":buf);
        sleep(3);
    }

    return 0;
}

5、结合前面知识使用守护进程检测程序未执行时启动程序

testpro.c

这个demo用于测试守护进程是否能够在该程序为执行时启动该程序

#include 
#include 

int main()
{
    int i = 0;
    while(1){
        printf("%d\n",i++);

        sleep(1);
    }
    return 0;
}

testdaemon.c

该守护进程不断进程testpro是否运行,没有运行的话则启动testpro,如果想要开机启动这个守护进程的话,需要将该程序路径复制到/etc/rc.local文件内

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

bool flags = true;

int judge()
{
        char buf[128]={'\0'};
        FILE *file;
        file = popen("ps -elf |grep testpro |grep -v grep","r");

        fread(buf,1,128,file);

        if(strstr(buf,"test") != NULL){
            return 1;
        }else{
            return 0;
        }

}

void handle(int signum,siginfo_t *infomation,void *context)
{
    printf("get signal %d,i am quit\n",signum);
    flags = false;
}

int main()
{
    int fd;
    struct sigaction act;
    time_t t;
    char *buf;

    if(daemon(0,0) == -1){
        printf("daemon error!\n");
        exit(-1);
    }

    act.sa_sigaction = handle;
    act.sa_flags = SA_SIGINFO;
    if(sigaction(SIGQUIT,&act,NULL) == -1){
        printf("sigaction error!\n");
        exit(-2);
    }

    while(flags){
        if(judge() == 0){
            system("/home/orangepi/code/testpro &");
        }
        sleep(5);
    }


    return 0;
}

6、做UDEV规则文件实现U盘自动挂载 

参考博文1

参考博文2

规则文件是udev里最重要的部分,默认是存放在/etc/udev/rule.d路劲下。所有的规则文件必须以

“.rules”为后缀名

下面是一个简单的规则:

KERNEL=="sda", NAME="my_root_disk", MODE=“0666” 

 KERNEL是匹配键,NAME和MODE是赋值键。这条规则的意思是,如果一个设备的内核名称为sda,则条件生效,执行后面的赋值:在/dev下产生一个名为my_root_disk的设备文件,并把设备文件的权限设为0660;

udevadm info --attribute-walk --name=/dev/设备文件

udevadm info --attribute-walk --name=/dev/bus/usb/001/005

udev 规则的匹配键
ACTION:事件(uevent)的行为,例如:add(添加设备)、remove(删除设备);

KERNEL:内核设备名称,例如:sda,cdrom; DEVPATH:设备的 devpath 路径;

SUBSYSTEM:设备的子系统名称,例如:sda 的系统为 block;


BUS:设备在 devpath 里的总线名称,例如:usb;

DRIVER:设备在 devpath 的设备驱动名称,例如:ide-cdrom;

ID:设备在 devpath 里的识别号;

SYSFS{filename}:设备的 devpath 路径下,设备的属性文件 "filename" 里的内容; 

ENV{key}:环境变量。在一条规则中,可以设定最多五条环境变量的匹配键;

PROGRAM:调用外部命令;

RESULT:外部命令 PROGRAM 的返回结果。

插入U盘后,可以使用dmesg指令来查看usb口的简陋信息

Linux-ARM架构,全志H616香橙派开发笔记_第20张图片 使用udevadm info --attribute-walk --name=/dev/设备文件,可以看设备的详细信息

Linux-ARM架构,全志H616香橙派开发笔记_第21张图片

当不使用udev来自动挂载U盘时,使用指令mount和umount来实现挂载和解除挂载

Linux-ARM架构,全志H616香橙派开发笔记_第22张图片

upan.rules 

u盘自动挂载规则文件

ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}+="/bin/mkdir /media/%k" ,RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode /media/%k"

ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", RUN{program}+="/bin/mkdir /media/%k" ,

RUN{program}+="/usr/bin/systemd-mount --no-block --collect $devnode /media/%k"

规则文件意思:事件是添加设备,子系统名称是usb,子系统名称是block,执行指令mkdir创建文件夹,执行指令mount挂载U盘到路径下

测试如下

插上U盘 

Linux-ARM架构,全志H616香橙派开发笔记_第23张图片

拔下U盘

Linux-ARM架构,全志H616香橙派开发笔记_第24张图片

这里用到tree指令

使用指令sudo apt-get install tree        安装tree的工具

七、嵌入式数据库

1、SQLite简介

轻量化,易用的嵌入式数据库,用于设备端的数据管理,可以理解成单点的数据库。传统服务器型数据 库用于管理多端设备,更加复杂

SQLite是一个无服务器的数据库,是自包含的。这也称为嵌入式数据库,这意味着数据库引擎作 为应用程序的一部分运行。

MySQL需要运行服务器,MySQL将需要客户端和服务器架构通过网络进行交互。

SQLite的优点:基于文件,易于设置和使用 ,适合基础开发和测试轻松携带,使用标准SQL语法进 行微小更改,使用方便

SQLite的缺点:缺乏用户管理和安全功能,不容易扩展,不适合大数据库,无法定制

MySQL的优点:使用方便,提供了许多与数据库相关的功能 ,良好的安全功能 ,易于扩展,适用于大型 数据库,提供良好的速度和性能,提供良好的用户管理和多种访问控制

MySQL的缺点:需要一些技术专业知识来设置 ,与传统SQL相比,语法略有不同

基于嵌入式的数据库主要有:SQLite,Firebird,Berkeley DB,eXtremeDB

Firebird 是关系型数据库,功能强大,支持存储过程,SQL兼容等

SQLite 关系型数据库,体积小,支持ACID事务

Berkeley DB 并没有数据库服务器的概念,他的程序直接链接到应用程序中

eXtremeDB 是内存数据库,运行效率高

2、SQLite数据库安装

安装方法1:

指令:sudo apt-get install sqlite

安装方法2:
下载地址:SQLite Download Page

1. 把下载的文件sqlite-autoconf-3390000.tar.gz上传到开发板
 

2. tar xvf sqlite-autoconf-3390000.tar.gz 解压
 

3. cd sqlite-autoconf-3390000 进入文件夹

4. ./configure --prefix=/usr/local 配置安装路径在/usr/local

5. make 编译//比较久10分钟

6. sudo make install 安装 

如下图指令测试,安装已经完成了 

Linux-ARM架构,全志H616香橙派开发笔记_第25张图片

3、SQLite的命令用法

①打开或创建一个数据库

方法一

1、sqlite3                //进入数据库

2、.open test.db      //当数据库没有时创建数据库

3、.quit                    //退出数据库命令行

方法二

1、sqlite3 mydatabase.db                //进入数据库命令行时顺带创建或打开

2、.databases                                   //列出当前打开的数据库

3、.quit                                             //退出 

②创建一张数据表

create table stu(id Integer,name char,score integer);
创建了一张叫做stu的数据表,里面包含了id,名字和成绩等信息

 ③插入数据

insert into stu values(001,"chenpeixin",60);

insert into stu values(002,'pengkaifan',59);    //‘’和“”都可以
insert into stu(id,score) values(004,99);         //只添加Id和分数

④查找数据表内的数据

select *from stu;                        //查询表内所有内容

select name,id from stu;           //查询表内名字、id

.tables                                       //可以查看打开的数据库里的表格

⑤出现异常 

 CTRL+Z退出程序

⑥删除一条数据

delete from stu where id=1;         //删除id=1的数据

⑦删除一张表 

drop table stu2;                                //删除表stu2

⑧更新表中数据 

update stu set name='chenpeixin' where id=4;     //更新id=4的数据的name更改为chenpeixin

update stu set sex='f' where id<10;                     //更新stu表中id<10的sex属性为f

⑨数据表增加一列属性 

alter table stu add column sex char;         //数据表增加一列sex的属性

4、SQLite的编程实战 

打开/创建数据库API

#include

int sqlite3_open(const char *filename,sqlite3 **ppDb);

函数作用:打开/创建数据库

filename:路径(包含文件名)

ppDb:sqlite3类型的二级指针,成功调用sqlite3_open()函数,ppDb将会指向数据库的地址

返回值:成功返回0(SQLITE_OK),失败返回错误编码

 

int sqlite3_close(sqlite3 *pDb);

函数作用:关闭数据库

pDb:sqlite3类型的指针,pDb将会指向数据库

返回值:成功返回0(SQLITE_OK),失败返回错误编码


const char *sqlite3_errmsg(qlite3 *pDb);
函数作用
:用来获取最近调用的数据库API接口返回的错误信息

pDb:sqlite3类型的指针,pDb将会指向数据库

返回值:错误信息(字符串)

int sqlite3_errcode(qlite3 *pDb);

函数作用:用来获取最近调用的数据库API接口返回的错误代码

pDb:sqlite3类型的指针,pDb将会指向数据库

返回值:错误代码

Linux-ARM架构,全志H616香橙派开发笔记_第26张图片

sqlite3_demo1.c 

#include 
#include 

int main(int argc,char **argv)
{
        sqlite3 *db;
        int ret;

        if(argc !=2){
                printf("error:%s xxx.db\n",argv[0]);
                return -1;
        }

        if((ret = sqlite3_open(argv[1],&db)) == SQLITE_OK){
                printf("open %s success! ret is %d,errmsg:%s,errcode:%d\n",argv[1],ret,sqlite3_errmsg(db),sqlite3_errcode(db));
        }else{
                printf("open %s fail! ret is %d,errmsg:%s,errcode:%d\n",argv[1],ret,sqlite3_errmsg(db),sqlite3_errcode(db));
                return -2;
        }

        if((ret = sqlite3_close(db)) == SQLITE_OK){
                printf("close %s success\n",argv[1]);
        }else{
                printf("close %s fail,errcode:%d\n",argv[1],sqlite3_errcode(db));
        }
        return 0;
}

Linux-ARM架构,全志H616香橙派开发笔记_第27张图片

执行命令语句API

#include

int sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg);
函数作用


sqlite3:sqlite3类型的指针,指向数据库

sql:sql语句

sqlite_callback:回调函数

data:传递给回调函数的第一个形参

errmsg:char类型二级指针,执行sqlite3_exec()返回错误信息

返回值:成功返回0,失败返回错误代码

回调函数原型:
int callback(void *arg, int column_size, char *column_value[],
                        char *column_name[]);
函数作用


arg:sqlite3_exec()函数传递的第四个参数

column_size:数据库的字段数

column_value[]:指针数组,列值

column_name[]:指针数组,字段名字

返回值:返回值必须是0,否则回调函数不会再次被执行

sqlite3_exec_demo1.c 

#include 
#include 




int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
        int i;
        printf("%s\n",(char *)arg);
        for(i= 0;i

 当把回调函数中的返回值改为1时,sqlite3_exec()函数返回错误代码,errmsg返回错误信息

Linux-ARM架构,全志H616香橙派开发笔记_第28张图片

sqlite3_exec_demo2.c

#include 
#include 
#include 




int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
        int i;
        printf("%s\n",(char *)arg);
        for(i= 0;i

八、基于Linux的智能垃圾桶项目

1、python基础

①环境搭建

安装python3.9
 


1、访问源列表里的每个网址,并将软件列表保存在本地电脑

sudo apt update

2、安装编译Python需要用到的环境
sudo apt install -y build-essential zlib1g-dev \

libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev \

libreadline-dev libffi-dev curl libbz2-dev

3、下载Python源码
wget \

https://www.python.org/ftp/python/3.9.10/Python-3.9.10.tgz

4、解压源码包
tar xvf Python-3.9.10.tgz

5、配置

./configure --enable-optimizations

6、采用4核的方式编译

make -j4

7、安装
sudo make install

以上完成了python的安装,路径默 认是/usr/local/bin

-------------------------------------------------------------------------------------------------------------------------
此时通过python --version查看当前链接的python版本为2.7,为了后续写代码调试方便,需要将python软链接到python3.9
1、首先删除/usr/bin/目录底下的python
sudo rm -f /usr/bin/python

2、建立python3.9的软链接
sudo ln -s /usr/local/bin/python3.9 /usr/bin/python
-------------------------------------------------------------------------------------------------------------------------
pip3是python3用来管理包的工具,可以用来安装、升级、卸载第三方库,默认它的源指向国外,有时候访问会非常慢导致失败,所以最好安装下自己的pip
sudo apt install -y python-pip python3-pip

修改源
mkdir -p ~/.pip 建立pip工作文件夹
vim ~/.pip/pip.conf 添加pip服务器配置文件
内容如下: [global]
timeout = 6000
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
trusted-host = pypi.tuna.tsinghua.edu.cn]

2、学会C调用python

菜鸟教程

你可能感兴趣的:(linux,arm)