ARM开发板学习

发布时间 2023-10-02 16:18:35作者: ⭐⭐-fighting⭐⭐

ARM开发板学习

1、蜂鸣器配饰和时间函数开发

#include <stdio.h>
#include <wiringPi.h>
#include <unistd.h>
#define BEEP 0 // 设置针脚0为蜂鸣器的控制引脚
int main(void)
{
    wiringPiSetup();       // 初始化wiringPi库i
    pinMode(BEEP, OUTPUT); // 设置IO口的输入输出,输出
    while (1)
    {
        // sleep(1);
        usleep(100000);
        digitalWrite(BEEP, HIGH); // 设置IO口输出低电平,蜂鸣器响
        // sleep(1);
        usleep(100000);
        digitalWrite(BEEP, LOW); // 设置IO口输出低电平,蜂鸣器响
    }
    return 0;
}

建议编译的shell脚本:

gcc $1 -lwiringPi -lwiringPiDev -lpthread -lm -lcrypt -lrt

2、超声波测距

时间函数

gettimeofday() 是 C 语言中的一个函数,用于获取当前时间和/或时区信息。通常用于测量时间间隔或为事件添加时间戳。

以下是 gettimeofday() 的详细用法:

函数签名

#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);

参数

  • struct timeval *tv:指向结构体(struct timeval)的指针,用于保存当前时间的秒数和微秒数。
  • struct timezone *tz:指向结构体(struct timezone)的指针,可以保存当前时区信息。然而,在现代用法中,此参数已过时,应将其设为 NULL。

返回值

  • 成功时返回 0。
  • 失败时返回 -1,并设置 errno 以指示错误。

gettimeofday()会把目前的时间用 tv 结构体返回,当地时区的信息则放到tz所指的结构

struct timeval
{
    long tv_sec;  /*秒*/
    long tv_usec; /*微妙*/
};

示例代码

//使用它来获取当前时间的秒数和微秒数
#include <sys/time.h>
#include <stdio.h>

int main() {
    struct timeval current_time;
    
    // 调用 gettimeofday 获取当前时间
    if (gettimeofday(&current_time, NULL) == 0) {
        printf("秒数: %ld\n", current_time.tv_sec);
        printf("微秒数: %ld\n", current_time.tv_usec);
    } else {
        perror("gettimeofday");
        return 1;
    }
    
    return 0;
}
// 计算程序在当前环境中数数10万次耗时多少
#include <sys/time.h>
#include <stdio.h>
// int gettimeofday(struct timeval *tv,struct timezone *tz )
void mydelay()
{
    int i, j;
    for (i = 0; i < 100; i++)
    {
        for (j = 0; j < 1000; j++);
    }
}
int main()
{
    struct timeval startTime;
    struct timeval stopTime;
    gettimeofday(&startTime, NULL);
    mydelay();
    gettimeofday(&stopTime, NULL);

    long diffTime = 1000000 * (stopTime.tv_sec - startTime.tv_sec) +
                    (stopTime.tv_usec - startTime.tv_usec);
    printf("Linux数100000耗时%ldus\n", diffTime);
    return 0;
}

测距代码

#include <stdio.h>
#include <sys/time.h>
#include <wiringPi.h>
#include <stdlib.h>

#define Trig 0
#define Echo 1

double getDistance()
{
    struct timeval start;
    struct timeval stop;

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

    distalWrite(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);
    printf("diffTime = %ld\n", diffTime);
    double dis = (double)diffTime / 1000000 * 334000 / 2;

    return dis;
}

int main()
{
    double dis;
    dis = getDistance();
    printf("distance = %d",dis);
}

3、SG90舵机开发

Linux定时器

分析:实现定时器,通过itimerval结构体以及函数setitimer产生的信号,系统随之使用signal信号处理 函数来处理产生的定时信号,从而实现定时器。

setitimer() 函数用于设置定时器,它可以在指定的时间间隔内定期产生信号,通常用于实现定时操作。下面是 setitimer() 函数的详细用法:

函数签名

#include <sys/time.h>

int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);

参数

  • int which:指定定时器类型,可以是 ITIMER_REALITIMER_VIRTUALITIMER_PROF

    • ITIMER_REAL:以系统真实时间计算,当计时器到期时发送 SIGALRM 信号。
    • ITIMER_VIRTUAL:进程执行的实际时间,不包括内核运行时间,发送的信号是SIGVTALRM
    • ITIMER_PROF:进程在用户态和内核态执行的总时间,发送的信号是SIGPROF
  • const struct itimerval *new_value:指定新的定时器设置,包括定时器的启动时间和间隔时间。

    • it_value:第一次定时器到期的时间。
    • it_interval:定时器间隔时间,即定时器到期后,再次定时的时间间隔。
  • struct itimerval *old_value(可选):用于存储之前的定时器设置,如果不需要,可以将其设置为 NULL。

结构体 struct itimerval

struct itimerval {
    struct timeval it_interval;  // 定时器间隔时间
    struct timeval it_value;     // 第一次定时器到期的时间
};

第一次定时器到期时间的设置是很重要的,因为它决定了定时器何时开始触发。定时器间隔时间定义了定时器触发的间隔

结构体 struct timeval

struct timeval {
    long tv_sec;  // 秒
    long tv_usec; // 微秒
};

返回值

  • 成功时返回 0,失败时返回 -1。

示例用法

下面是一个简单的示例,演示如何使用 setitimer() 来设置定时器:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <signal.h>

void timer_handler(int signum) {
    printf("Timer expired!\n");
}

int main() {
    struct itimerval timer;
    
    // 设置定时器间隔为 2 秒
    timer.it_interval.tv_sec = 2;
    timer.it_interval.tv_usec = 0;

    // 设置第一次定时器到期时间为 1 秒
    timer.it_value.tv_sec = 1;
    timer.it_value.tv_usec = 0;

    // 注册信号处理函数
    signal(SIGALRM, timer_handler);

    // 启动定时器
    if (setitimer(ITIMER_REAL, &timer, NULL) == -1) {
        perror("setitimer");
        exit(EXIT_FAILURE);
    }

    // 等待定时器触发,程序继续运行
    while (1) {
        sleep(1);
        printf("Waiting...\n");
    }

    return 0;
}

在这个示例中,我们创建了一个定时器,每隔 2 秒触发一次,并且在第一秒时触发。当定时器触发时,它将调用 timer_handler 函数。在主循环中,我们简单地等待定时器的触发。注意,定时器触发时将产生 SIGALRM 信号,该信号将由 timer_handler 处理函数处理。

舵机实现代码

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>
#include <signal.h>
#include <wiringPi.h>

#define SG90Pin 5

int jd;
static int i = 0;

void signal_handler(int signum)
{
    if (i <= jd)
    {
        digitalWrite(SG90Pin, HIGH);
    }
    else
    {
        digitalWrite(SG90Pin, LOW);
    }

    if (i == 40)
    {
        i = 0;
    }
    i++;
}

int main()
{
    struct itimerval itv;
    jd = 0;
    wiringPiSetup();
    pinMode(SG90Pin, OUTPUT);

    // 设定定时时间
    itv.it_interval.tv_sec = 0;
    itv.it_interval.tv_usec = 500;

    // 设定开始生效,启动定时器的时间
    itv.it_value.tv_sec = 1;
    itv.it_value.tv_usec = 0;

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

    signal(SIGALRM, signal_handler);

    while (1)
    {
        printf("input jd: 1-0 2-45 3-90 4-135 \n");
        scanf("%d", &jd);
    }
    return 0;
}

4、OLED屏应用-IIC协议

在树莓派下要先确保IIC模块打开;sudo raspi-config

image

开发代码
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdint.h>

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

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

	oled_putstrto(disp, 0, 10, "Welcome to My HomeAssitant");
	disp->font = font1;
	oled_putstrto(disp, 0, 20, "---Mr.zhou henshuai---");
	disp->font = font2;

	
	oled_send_buffer(disp);

return 0;
}

void show_error(int err, int add) {
	//const gchar* errmsg;
	//errmsg = g_strerror(errno);
	printf("\nERROR: %i, %i\n\n", err, add);
	//printf("\nERROR\n");
}

void show_usage(char *progname) {
	printf("\nUsage:\n%s <I2C bus device node >\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;
	disp.font = font2;

	e = oled_open(&disp, filename);

	if (e < 0) {
		show_error(1, e);
	} else {
		e = oled_init(&disp);
	if (e < 0) {
		show_error(2, e);
	} else {
		oled_demo(&disp);
		}
	}

	return 0;
}

5、Linux串口开发

树莓派串口配置参考博文

基于wiringPi的串口开发
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <wiringPi.h>
#include <wiringSerial.h>
#include <stdlib.h>
int fd;
void *Sendhandler()
{
    char *sendBuf;
    sendBuf = (char *)malloc(32 * sizeof(32));
    while (1)
    {
        memset(sendBuf, '\0', 32);
        scanf("%s", sendBuf);
        while (*sendBuf)
        {
            serialPutchar(fd, *sendBuf++);
        }
    }
}
void *Revhandler()
{
    while (1)
    {
        while (serialDataAvail(fd))
        {
            printf("%c", serialGetchar(fd));
            fflush(stdout);
        }
    }
}
int main()
{
    int count;
    unsigned int nextTime;
    pthread_t idSend;
    pthread_t idRev;
    if ((fd = serialOpen("/dev/ttyAMA0", 115200)) < 0)
    {
        fprintf(stderr, "Unable to open serial device: %s\n", strerror(errno));
        return 1;
    }
    pthread_create(&idSend, NULL, Sendhandler, NULL);
    pthread_create(&idRev, NULL, Revhandler, NULL);
    if (wiringPiSetup() == -1)
    {
        fprintf(stdout, "Unable to start wiringPi: %s\n", strerror(errno));
        return 1;
    }
    while (1)
    {
        sleep(10);
    }
    printf("\n");
    return 0;
}

6、语音控制刷抖音小项目

1、编程实现语音和开发板通信

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include "uartTool.h"
int fd;
void *readSerial()
{
    char cmd;
    while (1)
    {
        cmd = myserialGetchar(fd);
        switch (cmd)
        {
        case 'N':
            printf("next\n");
            break;
        case 'P':
            printf("pre\n");
            break;
        case 'Z':
            printf("zan\n");
            break;
        case 'Q':
            printf("qu\n");
            break;
        }
    }
}
int main(int argc, char **argv)
{
    char deviceName[32] = {'\0'};
    pthread_t readt;
    if (argc < 2)
    {
        printf("uage:%s /dev/ttyS?\n", argv[0]);
        return -1;
    }
    strcpy(deviceName, argv[1]);
    if ((fd = myserialOpen(deviceName, 115200)) == -1)
    {
        printf("open %s error\n", deviceName);
        return -1;
    }

    pthread_create(&readt, NULL, readSerial, NULL);
    while (1)
    {
        sleep(10);
    }
    return 1;
}

2、手机接入Linux热拔插相关

  • a. 把手机接入开发板
  • b. 安装adb工具,在终端输入adb安装指令: sudo apt-get install adb
  • c. dmeg能查看到手机接入的信息,但是输入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. 手机弹出调试提醒,点确认手机调试模式

3、用shell指令来操作手机屏幕,模拟手动划屏幕

  • adb 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 锁屏

4、最后的程序

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pthread.h>
#include "uartTool.h"
int fd;
void *readSerial()
{
    char cmd;
    while (1)
    {
        cmd = myserialGetchar(fd);
        switch (cmd)
        {
        case 'N':
            printf("next\n");
            system("adb shell input swipe 540 1300 540 500 100");
            break;
        case 'P':
            printf("pre\n");
            system("adb shell input swipe 540 500 540 1300 100");
            break;
        case 'Z':
            printf("zan\n");
            system("adb shell \"seq 3 | while read i;do input tap 350 1050 &
                       input tap 350 1050 &
                       sleep 0.01;
                   done;\"");
            break;
        case 'Q':
            printf("qu\n");
            system("adb shell input keyevent 26");
            break;
        }
    }
}
int main(int argc, char **argv)
{
    char deviceName[32] = {'\0'};
    pthread_t readt;
    if (argc < 2)
    {
        printf("uage:%s /dev/ttyS?\n", argv[0]);
        return -1;
    }
    strcpy(deviceName, argv[1]);
    if ((fd = myserialOpen(deviceName, 115200)) == -1)
    {
        printf("open %s error\n", deviceName);
        return -1;
    }
    pthread_create(&readt, NULL, readSerial, NULL);
    while (1)
    {
        sleep(10);
    }
}

7、Linux热拔插UDEV机制

简介

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

image

UDEV的配置文件

参考博文1 参考博文2

规则文件是 udev 里最重要的部分,默认是存放在 /etc/udev/rule.d/ 下。所有的规则文件必须以 ".rules" 为后缀名。

下面是一个简单的规则:

KERNEL=="sda", NAME="my_root_disk", MODE="0660"

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

udevadm info --attribute-walk --name=/dev/设备名字 可以看到设备的详细信息

SUBSYSTEM=="usb", ATTRS{idVendor}=="2a70", ATTRS{idProduct}=="4ee7",
MODE="0666"

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盘

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

8、守护进程

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

UDEV守护进程,它能够根据系统中的硬件设备的状态动态更新设备文件,包括设备文件的创建,删除 等。

基本特点

  • 生存周期长[非必须],一般操作系统启动的时候就启动,关闭的时候关闭。

  • 守护进程和终端无关联,也就是他们没有控制终端,所以当控制终端退出,也不会导致守护进程退 出

  • 守护进程是在后台运行,不会占着终端,终端可以执行其他命令

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

image

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

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

  • 老祖宗init:也是系统守护进程,它负责启动各运行层次特定的系统服务;所以很多进程的PPID是init,也 负责收养孤儿进程。

  • cmd列中名字不带[]的普通守护进程(用户集守护进程)

1、守护进程开发

1、守护进程也称为精灵进程(daemon),是运行在后台的一种特殊进程。它独立于控制终端并且周期性的执行某种任务或等待某些发生的事件。守护进程是一种很有用的进程。Linux的大多数服务器就是用守护进程实现的。比如:ftp服务器,ssh服务器,web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond等。
2、Linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不能直接和用户交互。其他进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但是系统服务进程(守护进程)不受用户登录和注销的影响,它们一直运行着。这种进程有一个名称叫守护进程(daemon)。
3、用ps -axj命令查看系统中的进程。参数a表示不仅列当前用户的进程,也列出所有其他用户的进程;参数x表示不仅列出有控制终端的进程,也列出所有无控制终端的进程;参数j表示列出与作业控制相关的进程。
image

解释:
(1)凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。
(2)在COMMAND一列用[]括起来的名字表示内核线程,这些线程在内核里创建,没有用户控件代码,因此没有程序文件名和命令行,通常采用以k开头的名字,表示Kernel。
(3)init进程是所有文件的父进程,udevd负责维护/dev目录下的设备文件,acpid负责电源管理syslogd负责维护/var/log下的日志文件。
(4)守护进程采用以d结尾的名字,表示daemon。

#include <unistd.h>
int daemon(int nochdir, int noclose);

函数参数:
nochdir:为0时表示将当前目录更改至“/”
noclose:为0时表示将标准输入、标准输出、标准错误重定向至“/dev/null”
返回值:
成功则返回0,失败返回-1

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
// C 库函数 char *asctime(const struct tm *timeptr) 返回一个指向字符串的指针,它代表了结
// 构 struct timeptr 的日期和时间。
// C 库函数 struct tm *localtime(const time_t *timer) 使用 timer 的值来填充 tm 结构。
// timer 的值被分解为 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; 夏令时
};
*/
//每相隔会在/home/pi/daemon.log生成系统时间
static bool flag = true;
void handler(int sig)
{
    printf("I got a signal %d\nI'm quitting.\n", sig);
    flag = false;
}
int main()
{
    time_t t;
    int fd;
    // 创建守护进程
    if (-1 == daemon(0, 0))
    {
        printf("daemon error\n");
        exit(1);
    }
    // 设置信号处理函数
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGQUIT, &act, NULL))
    {
        printf("sigaction error.\n");
        exit(0);
    }
    // 进程工作内容
    while (flag)
    {
        fd = open("/home/pi/daemon.log", O_WRONLY | O_CREAT | O_APPEND,
                  0644);
        if (fd == -1)
        {
            printf("open error\n");
        }
        t = time(0);
        char *buf = asctime(localtime(&t));
        write(fd, buf, strlen(buf));
        close(fd);
        sleep(10);
    }
    return 0;
}

sudo vi /etc/rc.local 开机自启动,绝对路径加程序名字

2、守护进程应用

守护进程和后台进程的区别

  1. 守护进程和终端不挂钩;后台进程能往终端上输出东西(和终端挂钩);

  2. 守护进程关闭终端时不受影响,守护进程不会随着终端的退出而退出;

判断程序是否在运行

#include <stdio.h>
#include <string.h>
int main()
{
    FILE *file;
    char buffer[128] = {'\0'};
    char *cmd = "ps -elf |grep douyin|grep -v grep";
    file = popen(cmd, "r");
    fgets(buffer, 128, file);
    if (strstr(buffer, "douyin") != NULL)
    {
        printf("douyinPro is running\n");
    }
    else
    {
        printf("douyinPro is not running\n");
    }
    printf("BUFFER:%s\n", buffer);
}

守护进程不让控制程序退出

#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdbool.h>
static bool flag = true;
void handler(int sig)
{
    printf("I got a signal %d\nI'm quitting.\n", sig);
    flag = false;
}
int judMent()

{
    FILE *file;
    char buffer[128] = {'\0'};
    char *cmd = "ps -elf |grep douyinUtils|grep -v grep";
    file = popen(cmd, "r");
    fgets(buffer, 128, file);
    if (strstr(buffer, "douyinUtils") != NULL)
    {
        return 0;
    }
    else
    {
        return -1;
    }
    printf("BUFFER:%s\n", buffer);
}
int main()
{
    time_t t;
    int fd;
    // 创建守护进程
    if (-1 == daemon(0, 0))
    {
        printf("daemon error\n");
        exit(1);
    }
    // 设置信号处理函数
    struct sigaction act;
    act.sa_handler = handler;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    if (sigaction(SIGQUIT, &act, NULL))
    {
        printf("sigaction error.\n");
        exit(0);
    }
    // 进程工作内容
    while (flag)
    {
        if (judMent() == -1)
        {
            system("/home/orangepi/hardwareSoft/douyin/douyinUtils /dev/ttyAMA0 &");
        }
        sleep(2);
    }
    return 0;
}

开机启动

sudo vi /etc/rc.local
/home/orangepi/hardwareSoft/douyin/douyinUtils /dev/ttyS5 &
/home/orangepi/hardwareSoft/douyin/shouhuDouyin
exit 0

9、SQLite数据库

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

  • SQLite是一个无服务器的数据库,是自包含的。这也称为嵌入式数据库,这意味着数据库引擎作 为应用程序的一部分运行。
  • MySQL需要运行服务器,MySQL将需要客户端和服务器架构通过网络进行交互。

image

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

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

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

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

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

image

SQLite的命令用法

创建一个数据库

方式一

  1. sqlite3 进入数据库
  2. .open test.db
  3. .quit

数据库退出后在命令当前了路径创建数据库test.db

方式二

  1. sqlite3 test.db //在命令运行当前你窗口创建数据库test.db
  2. 在数据库命令下
  3. .databases 列出当前打开的数据库
  4. .quit 退出

创建一张表格

create table stu2(id Integer,name char,score Integer);

插入一条记录

insert into stu values(18130106,'huang',99);
insert into stu2 values(18130101,"gang",100);   ''和""都行
insert into stu(name,score) values("huanggang",98); 插入部分字段内容

查看数据库的记录

select * from stu; //查询所有字段的结果
select name,score from stu; //查询数据库中部分字段的内容

删除一条记录

delete from stu where id = 18130101;

更改一条记录

update stu set name = 'huangg' where id = 18130106;

删除一张表

drop table stu;

增加一列

alter table stu add column sex char;

SQLite的编程操作

编译的时候要加上 gcc -lsqlite3

1、打开/创建数据库的C接口

sqlite3_open(const char *filename, sqlite3 **ppDb)
//该例程打开一个指向 SQLite 数据库文件的连接,返回一个用于其他 SQLite 程序的数据库连接对象。
sqlite3_close(sqlite3*)
//该例程关闭之前调用 sqlite3_open() 打开的数据库连接。所有与连接相关的语句都应在连接关闭之前完成。
如果还有查询没有完成,sqlite3_close() 将返回 SQLITE_BUSY 禁止关闭的错误消息。
const char *sqlite3_errmsg(sqlite3*);
//sqlite3_errcode() 通常用来获取最近调用的API接口返回的错误代码
#include <stdio.h>
#include <sqlite3.h>
int main(char argc, char **argv)
{
    sqlite3 *db;
    int ret;
    if (argc < 2)
    {
        printf("Usage: %s xxx.db\n", argv[0]);
        return -1;
    }
    if ((ret = sqlite3_open(argv[1], &db)) == SQLITE_OK)
    {
        printf("open %s success\n", argv[1]);
    }
    else
    {
        printf("error:%s,%d\n", sqlite3_errmsg(db), ret);
        if (ret == 14)
        {
            printf("permission den\n");
        }
        return -1;
    }
    sqlite3_close(db);
    printf("done\n");
    return 0;
}

2、创建表的C接口

闯将表的API

sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char
**errmsg)
  • sqlite3 是打开的数据库对象,
  • sqlite_callback 是一个回调,
  • data 作为其第一个参数,
  • errmsg 将被返回用来获取程序生成的任何错误。
sqlite3_exec() //程序解析并执行由 sql 参数所给的每个命令,直到字符串结束或者遇到错误为止。
int callback(void *arg, int column_size, char *column_value[], char
*column_name[])
  • void *arg:是sqlite3_exec函数的第四个参数
  • column_size:数据库的字段 数
  • column_value[]:列的值
  • column_name:字段名
#include <stdio.h>
#include <sqlite3.h>
int callback(void *arg, int column_size, char *column_value[], char *column_name[])
{
    int i;
    printf("arg=%s\n", (char *)arg);
    for (i = 0; i < column_size; i++)
    {
        printf("%s = %s\n", column_name[i], column_value[i]);
    }
    printf("=======================\n");
    return 0; // 必须返回0,这样数据库中有多少条数据,这个回调函数就会被调用多少次
}
int main(char argc, char **argv)
{
    sqlite3 *db;
    char *errorMes = NULL;
    int ret;
    if (argc < 2)
    {
        printf("Usage: %s xxx.db\n", argv[0]);
        return -1;
    }
    if ((ret = sqlite3_open(argv[1], &db)) == SQLITE_OK)
    {
        printf("open %s success\n", argv[1]);
    }
    else
    {
        printf("error:%s,%d\n", sqlite3_errmsg(db), ret);
        if (ret == 14)
        {
            printf("permission den\n");
        }
        return -1;
    }
    // sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void *data, char **errmsg)
    sqlite3_exec(db, "select * from stu;", callback, "content of sql:", &errorMes); // errorMes may sigment error!
    sqlite3_close(db);
    printf("done\n");
    return 0;
}
实现一条建立表格的指令
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
    int i;
    for (i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
}
int main(int argc, char *argv[])
{
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;
    char *sql;
    /* Open database */
    rc = sqlite3_open("test.db", &db);
    if (rc)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
    else
    {
        fprintf(stdout, "Opened database successfully\n");
    }
    /* Create SQL statement */
    sql = "CREATE TABLE COMPANY("
          "ID INT PRIMARY KEY NOT NULL,"
          "NAME TEXT NOT NULL,"
          "AGE INT NOT NULL,"
          "ADDRESS CHAR(50),"
          "SALARY REAL );";
    /* Execute SQL statement */
    rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
    }
    else
    {
        fprintf(stdout, "Table created successfully\n");
    }
    sqlite3_close(db);
    return 0;
}

3、插入数据的C接口

#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *NotUsed, int argc, char **argv, char **azColName)
{
    int i;
    for (i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}

int main(int argc, char *argv[])
{
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;
    char *sql;
    /* Open database */
    rc = sqlite3_open("test.db", &db);
    if (rc)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
    else
    {
        fprintf(stderr, "Opened database successfully\n");
    }
    /* Create SQL statement */
    sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "
          "VALUES (1, 'Paul', 32, 'California', 20000.00 ); "
          "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "
          "VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); "
          "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)"
          "VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );"
          "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)"
          "VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );";
    /* Execute SQL statement */
    rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
    }
    else
    {
        fprintf(stdout, "Records created successfully\n");
    }
    sqlite3_close(db);
    return 0;
}

4、SELECT操作

回调函数的一些细节,这将在我们的实例使用 到。这个回调提供了一个从 SELECT 语句获得结果的方式。它声明如下:

typedef int (*sqlite3_callback)(
void*, /* sqlite3_exec()的第四个参数传递的内容 */
int, /* 列 */
char**, /* 键值对的值 */
char** /* 键值对的键 */
);

如果上面的回调在 sqlite_exec() 程序中作为第三个参数,那么 SQLite 将为 SQL 参数内执行的每个 SELECT 语句中处理的每个记录调用这个回调函数。

下面的 C 代码段显示了如何从前面创建的 COMPANY 表中获取并显示记录:

#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *data, int argc, char **argv, char **azColName)
{
    int i;
    fprintf(stderr, "%s: ", (const char *)data);
    for (i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}
SQLdataToLink()
    LinkToSQL() int main(int argc, char *argv[])
{
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;
    char *sql;
    const char *data = "Callback function called";
    /* Open database */
    rc = sqlite3_open("test.db", &db);
    /* Create SQL statement */
    sql = "SELECT * from COMPANY";
    /* Execute SQL statement */
    rc = sqlite3_exec(db, sql, callback, (void *)data, &zErrMsg);
    sqlite3_close(db);
    return 0;
}

4、UPDATE操作

下面的 C 代码段显示了如何使用 UPDATE 语句来更新任何记录,然后从 COMPANY 表中获取并显示更 新的记录:

#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *data, int argc, char **argv, char **azColName)
{
    int i;
    fprintf(stderr, "%s: ", (const char *)data);
    for (i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}
int main(int argc, char *argv[])
{

    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;
    char *sql;
    const char *data = "Callback function called";
    /* Open database */
    rc = sqlite3_open("test.db", &db);
    if (rc)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
    else
    {
        fprintf(stderr, "Opened database successfully\n");
    }
    /* Create merged SQL statement */
    sql = "UPDATE COMPANY set SALARY = 25000.00 where ID=1; "
          "SELECT * from COMPANY";
    /* Execute SQL statement */
    rc = sqlite3_exec(db, sql, callback, (void *)data, &zErrMsg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
    }
    else
    {
        fprintf(stdout, "Operation done successfully\n");
    }
    sqlite3_close(db);
    return 0;
}

5、DELETE操作

下面的 C 代码段显示了如何使用 DELETE 语句删除任何记录,然后从 COMPANY 表中获取并显示剩余的 记录:

#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
static int callback(void *data, int argc, char **argv, char **azColName)
{
    int i;
    fprintf(stderr, "%s: ", (const char *)data);
    for (i = 0; i < argc; i++)
    {
        printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
}
int main(int argc, char *argv[])
{
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;

    char *sql;
    const char *data = "Callback function called";
    /* Open database */
    rc = sqlite3_open("test.db", &db);
    if (rc)
    {
        fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
        exit(0);
    }
    else
    {
        fprintf(stderr, "Opened database successfully\n");
    }
    /* Create merged SQL statement */
    sql = "DELETE from COMPANY where ID=2; "
          "SELECT * from COMPANY";
    /* Execute SQL statement */
    rc = sqlite3_exec(db, sql, callback, (void *)data, &zErrMsg);
    if (rc != SQLITE_OK)
    {
        fprintf(stderr, "SQL error: %s\n", zErrMsg);
        sqlite3_free(zErrMsg);
    }
    else
    {
        fprintf(stdout, "Operation done successfully\n");
    }
    sqlite3_close(db);
    return 0;
}