嵌入式开发-各种干货

1、Jar包上传到FTP
2、登录Ubuntu,修改jar包的属性sudo chmod 777 xxxxx.jar
3、运行jar   nohup java -jar xxxxxx.jar
4、结束服务: ps -e | grep java 查看java的进程号
5、sudo kill 进程号
nohup java -jar /srv/ftpdata/smu/www/shared_device_service-1.0-SNAPSHOT.jar & #后台运行

目录

第一章 C语言基础

注意事项

1.1链表的创建

如果创建一个表头指针后用malloc申请空间,结构体的值为0.

第五节 指针

1.5.1指针数组 与 数组指针 的区别

指针数组

存放一串 地址的 数组。

int a = 2;
int b = 3;
int c = 5;

int* p[3];

p[0] = &a;
p[1] = &b;
p[2] = &c;
数组指针

1、强调的是类型,数组的个数,偏移值是偏移了整个数组的大小。

2、包住了整个数组的所有地址的指针。该指针++后,地址增加数据类型的个数*数组的大小。

int a[3] = {1,2,3};

int (*p)[3];

p = a;

1.5.2函数指针

int fun(int a,int b){
    return a+b;
}

void printWelcome(){
    printf("欢迎来到嵌入式攻城狮大课堂!!\n");
}
int main(){
	int (*pfun)(int data,int data2);
    pfun = fun;
    
	pfun(1,2);  
    (*pfun)(1,2);
    //----------------------------
    void (*p2)();
    p2 = printWelcome;
    
    p2();
    (*p2)();
    
    return 0;
}

1.5.14 无类型指针&&malloc

1 malloc的介绍
malloc函数的原型

void* malloc(size_t,size);返回类型是void*,即:无类型的指针;**但可以强制转换(int *)、(char )、(double )…

malloc(32)申请空间

申请32个字节的空间,这32个字节是连续的,一个整块。

如果要存放整形(int)必须在前面加上(int *),即:(int *)malloc(32);

2 举例
int XX[3];	//数组 

int *p =XX;

int *a = (int *)malloc(3 * sizeof(int))//这也是数组,(* a,* p)两者一样的

1.5.16 指针的收官,面试题

1 指针面试题

1.定义整形变量 a

2.定义p为指向整形数据的指针变量

3.定义整型数组 a,它有5个元素

4.定义指针数组 p,它由4个指向整型数据的指针元素组成

5.p为指向包含4个元素的一堆数组的指针变量

6.f为返回整形函数值的函数

7.p为返回一个指针的函数,该指针指向整型数据

8.p为指向函数的指针,该函数返回一个整型值

9.p是一个指针变量,它指向一个指向整型数据的指针变量

10.p是一个指针变量,基类型为 void(空类型),不指向具体的对象

答案:(1)int a; (2)int *p; (3)int a[5]; (4)int *p[4];指针数组

(5)int (*p)[4];数组指针,跨越的是整个数组,16个字节 (6)int f();

(7)int* p(); (8)int (*p)(); (9)int **p; (10)void *p;

第六节 字符串

1.6.1字符串的定义方式、输出

四种定义方式
1char str[5] = {'a','b','c','d','e'};
2char str[5] = "abcde";
3char str[] = "abcde";***//长度 (用sizeof()求)= 字符个数 + 1,因为这种方式会在后面默认加上‘\0’。代表字符串的结束标志***
4、用指针定义:char *pstr = "abcdefghijk";
输出方式
1printf("%c\n",str[i]);//逐个输出

2printf("%s\n",pstr);//一次性输出

3puts(str);//和2一样,只是多了一个自动换行

1.6.2字符串常用函数、输入、输出

输入方式
1scanf("%s",str);
2gets(str);

第九节 链表

1.9.1特点

灵活

1.9.2链表和数组的区别

1、数组的地址是连续的,链表是不连续的。

2、链表只不过是它的next值指向一个地址。

第二章 贪吃蛇小游戏开发

2.1 ncurse的使用

#include <curses.h>							//头文件

initscr();									//ncurse界面的初始化函数

printw("This is curses windows\n");			//在ncurse模式下的printf

getch();	//等待用户输入,如果没有这句话,程序就退出了,看不到运行结果,也就是看不到上面那句话

endwin();	//程序退出,调用该函数来恢复shell终端的显示,如果没有这句话,shell终端会乱码,乱结构,坏掉。

2.2方向键的开发c

#define KEY_DOWN 0402
#define KEY_UP 0403
#define KEY_LEFT 0404 
#define KEY_RIGHT 0405
#define KEY_HOME 0406
#define KEY_BACKSPACE 0407 
#define KEY_F0 0410
#define KEY_F(n) (KEY_ F0+(n))
#define KEY_DL 0510
#define KEY_IL 0511
 
 int c;initscr();
    printw("this is a test\n");
    keypad(stdscr,1);while(1){
            c = getch();
            switch(c){
                    case KEY_DOWN:
                            printw("DOWN\n");
                            break;
                    case KEY_UP:
                            printw("UP\n");
                            break;
                    case KEY_LEFT:
                            printw("LEFT\n");
                            break;
                    case KEY_RIGHT:
                            printw("RIGHT\n");
                            break;
                    default :
                            printw("you input :%d\n",c);
                            break;}
    }
    getch();
    endwin();

2.3 各函数解析

void initFood(){
        food.hang = rand()%20;
        food.lie = rand()%20;
}
int hasFood(int i,int j){
        if(food.hang == i && food.lie == j){
                return 1;
        }
        return 0;
}
   int hasNode(int i,int j){//判断是否存在该节点
    struct Snake *p;
    p = head;
    while(p != NULL){
            if(p->hang == i && p->lie == j){
                    return 1;
            }
            p = p-> next;
    }
    return 0;
   }
void initNcurse(){//让键盘之直接取到方向值,即:去掉"^[["
        initscr();
        keypad(stdscr,1);
}
void gamePic(){//贪吃蛇地图构建
    int hang;
    int lie;
    move(0,0);//每次让光标移动到最左上角,刷新的时候保证图形一直在固定位置

    for (hang = 0;hang < 20;hang ++){
            if(hang == 0){
                    for(lie = 0;lie <= 20;lie ++){
                            printw("--");
                    }
                    printw("\n");
                    for(lie = 0;lie <= 21;lie ++){
                            if(lie == 0 || lie == 21){
                                    printw("|");
                            }
                            else if(hasNode(hang,lie)){
                                    printw("[]");//当该坐标(hang,lie)为蛇的身子时,打印出蛇身“[]”
                            }
                            else{
                                    printw("  ");
                            }
                    }
                    printw("\n");
            }

            if(hang > 0 && hang < 20){
                    for(lie = 0;lie <= 21;lie ++){
                            if(lie == 0 || lie == 21){
                                    printw("|");
                            }
                            else if(hasNode(hang,lie)){
                                    printw("[]");//同上
                            }
                            else{
                                    printw("  ");
                            }
                     }
                        printw("\n");
             }
             if(hang == 19){
                     for(lie = 0;lie < 21;lie ++){
                             printw("--");
                     }
                     printw("\n");
                     printw("Made by Du Shangkun\n");
             }
     }
}
void addNode(){//添加蛇身子
        struct Snake *new = (struct Snake *)malloc(sizeof(struct Snake));

        new->hang = tail->hang;
        new->lie = tail->lie + 1;//默认蛇身子向右移动
        tail->next = new;
        tail = new;

}
void intSnake(){//蛇的最初位置及长度
        head = (struct Snake *)malloc(sizeof( struct Snake));
        head->hang = 2;
        head->lie = 2;
        head->next = NULL;

        tail = head;
    
        addNode();
        addNode();
        addNode();
        addNode();

}
void deleNode(){//删除节点用的
        //struct Snake *p;
        //p = head;

        head = head -> next;
        //free(p);

}
void moveSnake(){//蛇身子的移动 <=> 添加新的节点,删掉旧的节点
//      struct Snake *p;
//      struct Snake *new;

        addNode();
        deleNode();
}
int main(){//主函数
        int con;//接受键盘指令
        initNcurse();

        intSnake();
    
        gamePic();
    
        while(1){
                con = getch();
                if(con == KEY_RIGHT){//激发右→时
                        moveSnake();
                        gamePic();
                }
        }
    
        getch();
        endwin();//必须要加,不然界面会乱掉
    
        return 0;
}

第三章 智能开盖垃圾桶开发

3.1、安装anduino

3.2、安装IO端口驱动程序

# include <Servo.h>
# define Echo D2
# define Trig D8
# define DuoPIN D4
# define BEEF D3
Servo myDuoji;

long getTime(){
  digitalWrite(Trig,HIGH);
  delayMicroseconds(10);//10微妙
  digitalWrite(Trig,LOW);
  return pulseIn(Echo,HIGH);
}

void initSuperVoice(){
  pinMode(Echo,INPUT);
  pinMode(Trig,OUTPUT);
}

void setup() {
  initSuperVoice();
  myDuoji.attach(DuoPIN);
  pinMode(BEEF,OUTPUT);//设置
  digitalWrite(BEEF,HIGH);
  Serial.begin(115200);
}

void loop() {
  //获取距离
  long distant;
  distant = getTime() / 58;
  if(distant <= 15){
    Serial.print(distant);
    Serial.print("cm");
    Serial.println();

    digitalWrite(BEEF,LOW);//蜂鸣器发声
    delay(500);
    digitalWrite(BEEF,HIGH);//蜂鸣器关闭

    Serial.println("open!");
    myDuoji.write(90); 
    delay(2000);
   }else{c
     digitalWrite(BEEF,HIGH);
     Serial.println("close!");
     myDuoji.write(180);
  }
}

3.3、Wemos D1开发板

常用函数

1、Serial.begin(115200);//开启端口

2、Serial.println();//输出

3、Serial.available();

代码展示

#include <Servo.h>

#define Echo D2
#define BEEF D3
#define Trig D8
#define DuoPIN D5

Servo myDuoji;

long getTime(){											//超声波的测距
      digitalWrite(Trig,HIGH);
      delayMicroseconds(10);//10微妙
      digitalWrite(Trig,LOW);
    
      return pulseIn(Echo,HIGH);
}

void initSuperVoice(){									//初始化超声波模块接口
      pinMode(Echo,INPUT);
      pinMode(Trig,OUTPUT);
}

void setup() {
	initSuperVoice();
	myDuoji.attach(DuoPIN);
	pinMode(BEEF,OUTPUT);//设置
	digitalWrite(BEEF,HIGH);
	Serial.begin(115200);
}

void loop() {
      //获取距离
	long distant;
	distant = getTime() / 58;

    if(distant <= 15){
		Serial.print(distant);
		Serial.print("cm");
		Serial.println();
		digitalWrite(BEEF,LOW);
		delay(500);
		digitalWrite(BEEF,HIGH);
		Serial.println("open!");

		myDuoji.write(90); 
		delay(1500);
    }else{
	 digitalWrite(BEEF,HIGH);
     //Serial.println("close!");
     myDuoji.write(180);
  }
}

注意事项

1、串口调试小助手的手动发送要调成16进制

第四章 智能避障小车开发

第五章 Linux 系统编程

第1节 Linux系统

5.1.1如何使用Linux做开发

1、Vi的使用
1>模式

命令行模式:(默认此模式)从输入模式回到命令行模式:ESC;ESC按完,insert消失。

输入模式:按 i 进入,看到insert就能编辑代码。

2>退出vi,保存代码

按:(冒号) + wq。

w:保存

q:退出

3、gcc编译工具
gcc	a.c	-o	X		//	(a.c是你要编译的c文件名字,X是生成的程序名)
4、运行

./程序名,eg:./a

5.1.2 常用命令

代码的复制粘贴

(命令模式下)要复制的行数+yy 复制代码

(命令模式下)行数 + dd 删除

(命令模式下)U 回到上一步

删除或者拷贝内容后,直接输入“p”,即可粘贴

dw     表示删除从当前光标到光标所在单词结尾的内容.
d0     表示删除从当前光标到光标所在行首的内容.
d$     表示删除从当前光标到光标所在行尾的内容.
dd     表示删除光标所在行.
6dd    表示删除6行
D      表示删除从当前光标到光标所在行尾的内容.
:6,10  delete   表示删除第6行至第10行的数据,其中“delete”可以简写为“d”或“del”等等。---删除大量行时使用
yw     表示拷贝从当前光标到光标所在单词结尾的内容.
y0     表示拷贝从当前光标到光标所在行首的内容.
y$     表示拷贝从当前光标到光标所在行尾的内容.
yfa    表示拷贝从当前光标到光标后面的第一个a字符之间的内容.

yy    表示拷贝光标所在行.
8yy   标识拷贝8行
:6,10 copy 18 
表示从第6行开始的5行(即从第6行到第10行)数据复制到了第18行下面,其中“copy”也可写为“co”或“cop”。---拷贝大量行时使用
操作命令
命令功能
ctrl + alt+ T打开命令行终端
xrandr显示可调的分辨率
xrandr -s + 分辨率调整分辨率
ctrl + L清屏
ls列出当前文件夹下有哪些文件
ls - a显示所有文件,包括隐藏文件
vi 文件名.c + 回车创建C类型的文件
pwd显示当前所在路径
mkdir + 文件名新建一个文件夹(同Windows系统)
cd + 回车直接回到工作路径:/home/User
cd + 文件夹名称进入某个文件夹
cd …回退到上一级
TAB键补全文件名
man 函数名显示函数名的用法
man 2 函数名显示函数名的用法
mv *.c MyDocument把.c类型的文件移动到MyDocument文件夹下;
mv a.c b.c将a.c命名位b.c
cp b.c test.c把b.c 拷贝成test.c
cp a.out /mnt/hgfs/CodeFromWindows/把a.out传到Win系统中
ifconfigIP地址(Linux)
ipconfigIP地址(Windows)
ls -l列举文件的清单
mv calculatorT.c ~将该文件移动到工作目录
cp ~/calculatorT.c .将该文件从工作目录移动到当前路径
file 编译文件名查看编译文件的运行环境,如ARM、X86-64等…
scp 文件名 pi@192.168.43.30:home/pi将文件远程拷贝到其他主机,开发板用户名@开发板地址:开发板的绝对路径。
kill -9 进程的PID杀死该进程
grep gcc * -nirgrep gcc 在所有的文件当中去匹配gcc字段,*代表所有的文件,-nir:n显示行号,i不区分大小写,r是递归,一个目录一个目录找进去
rm -r wiringpi/删除多文件夹目录的时候就要用到-r
history显示当前终端窗口用过的命令,
history |grep 匹配符显示所有敲过的带“匹配符”的历史命令
sudo apt-get install build-essential安装gcc编辑器
sudo apt-get install net-tools安装ifconfig
uname -r查看内核版本
while 指令whitch ls,查看ls指令所在目录
du 文件名计算文件大小(Kb)
gg=G整理代码格式
find -name 文件名查找文件

5.1.3 Windows和Linux的文件共享

共享文件的路径:
cd /mnt/hgfs/共享文件夹名称/

注意:一般不要在该路径下直接操作,会出现权限问题

解决办法:cp cd /mnt/hgfs/共享文件夹名称/FirstLinuxCode.c .

​ 将.c文件拷贝到工作路径(/home/CLC)

FTP:FileZilla Server

**Host:**localhost

**Port:**14147

**Password:**123

第2节 文件编程

全部指令:

ls -l列举当前路径的文件清单,及属性
- rwxr - xr - x横杠“-”:代表普通文件,r:read,w:write,x:可执行文件
rm 文件名删除文件

5.2.1打开文件

#include <sys/types.h>//头文件
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
fd = open("./file1",O_RDWR);						//打开这个文件,可以读和写入
fd = open("./file1",O_RDWR | O_CREAT,0600);			//打开这个文件,如果没有的话就创建,
/*
0600给文件的所有者相应的权限
1.可读		r		4
2.可写		w		2
3.执行		x		1

0600代表:4 + 2
*/

5.2.2 写入操作

char *buf = {"DuShangkun hen shuai!!!"};		//	要写入的字符串
fd = open("./file1",O_RDWR);					//打开一个文件
write(fd,buf,strlen(buf));						//写入操作,strlen(buf)为字符串的长度,不能用sizeof();
close(fd);

5.2.3 读取操作

char *buf = {"DuShangkun hen shuai!!!"};		//	要写入的字符串
fd = open("./file1",O_RDWR);					//打开一个文件
int n_write = write(fd,buf,strlen(buf));		//n_write是写入的字节数
/*___________________*/
char *readBuf;									//读取出来的字符存到该字符串里面
readBuf = (char *)malloc(sizeof(char)*n_write);//申请空间,防止野指针的出现
int n_read = read(fd,readBuf,n_write);
/*
这么写是对的,但是无法读取结果,原因就是光标在文件的尾巴,而read()只能读光标后面的字符。
解决方法:
1. 写入字符后,关闭fd,再重新打开。close(fd);在第四行输入该函数。
2. 不关闭fd,将光标移到最前面。见下一节
*/
printf("%s\n",readBuf);
close(fd);

5.2.4 文件“光标”位置

SEEK_SET(); //宏定义:字符头位置

SEEK_END(); //字符尾巴位置

SEEK_CUR(); //当前位置

char *buf = {"DuShangkun hen shuai!!!"};		//	要写入的字符串
fd = open("./file1",O_RDWR);					//打开一个文件
int n_write = write(fd,buf,strlen(buf));		//n_write是写入的字节数
char *readBuf;									//读取出来的字符存到该字符串里面
readBuf = (char *)malloc(sizeof(char)*n_write);//申请空间,防止野指针的出现

lseek(fd,0,SEET_SET);							//光标跳到距离头 0 个位置的地方 <=> 移到头,
/*
lseek(fd,2,SEET_SET);							//第二个参数>0表示在最前面的位置再向右移动2个位置
lseek(fd,-2,SEET_SET);							//<0表示向左移动,如果超边界,就移动到最边上
lseek(fd,-32,SEET_END);

返回值是光标移动的次数,所以,当打开一个文件,再将光标跳转到尾巴,可以计算出文件的大小。
*/

int n_read = read(fd,readBuf,n_write);
printf("%s\n",readBuf);
close(fd);

5.2.5 文件其他操作

函数原型

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int creat(const char *pathname, mode_t mode);

//Flags:这三个只能指定一个
O_RDONLY 		//只读打开
O_WRONLY 		//只写打开
O_RDWR	 		//可读可写打开
//-----------可通过 或'|' 的方式 添加以下功能------------------
O_CREAT	 
O_EXCL
O_APPEND	//每次写入时,都添加到文件的尾端,不会覆盖之前的内容
			fd = open("./file1",O_RDWR | O_APPEND);
			fd = open("./file1",O_RDWR);//会覆盖原来字节的位置,而不是把已有的全部删掉,
			//注意!!换行'\n'也算一个字符
O_TRUNC		//将内容全部清零

5.2.5 创建文件

  1. 控制台直接创建:touch 文件名称

    eg:touch file1

  2. 代码创建
    函数原型:
    int creat(const char *pathname, mode_t mode);
    
    常见创建模式:
宏表示数字功能
S_IRUSR4可读
S_IWUSR2可写
S_IXUSR1可执行
S_IRWXU7可读、写、执行
int fd;

fd = creat("/home/CLC/file1",S_IRWXU);

5.2.6 文件描述符

宏定义了,0,1,2;

0:标准输入;

1:标准输出;

2:错误

第3节 进程

ps显示所有进程
ps -aux
ps -aux|grep init
top
ipcs -m查看系统有哪些共享内存
ipcrm -m 共享内存ID删除某个共享内存

5.3.1 有面试技巧

5.3.2 fork

fork创建一个子进程的一 般目的
(1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络服务进程
中是常见的一父 进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子
进程处理此请求。父进程则继续等待下-个服务请求到达。
(2) -一个进程要执行一个不同的程序。这对shelI是常见的情况。在这种情况下,子进程从
fork返回后立即调用exec (我们将在8.10节说明exec)。

5.3.3 vfork

vfork函数也可以创建进程,与fork有什么区别
关键区别一:
vfork直接使用父进程存储空间,不拷贝。
关键区别二:
vfork保证子进程先运行,当子进程调用exit退出后,父进程才
执行。

5.3.4 正常退出

1… Main函数调用return
2.进程调用exit(),标准c库
3.进程调用_______exit(或者_______Exit(),. 属于系统调用
补充:

  1. 进程最后一个线程返回
    2.最后一个线程调用pthread exit

5.3.5 父进程等待子进程退出(一)

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main(){
    pid_t pid;
    int cnt = 0;
    int status = 10;

    pid = fork();

    if(pid > 0){
            wait(&status);/*
            wait();当子进程没有全部结束时,主进程会一直堵塞在这里,直到子进程结束,返回一个exit()值给status
            */
            printf("child qiut ,status = %d\n",WEXITSTATUS(status));//宏转换,传给Exit函数
            while(1){
                    printf("this is father print,pid = %d\n",getpid());
                    sleep(3);
                    printf("cnt = %d\n",cnt);
            }
    }
    else if(pid == 0){
            while(1){
                    printf("this is child print.pid = %d\n",getpid());
                    sleep(3);
                    cnt++;
                    if(cnt == 3){
                            exit(4);
                    }
            }
    }

    return 0;
}

第4节 进程间的通讯

5.4.2 无名管道 FIFO

第6节 网络编程

TCP/UDP对比

  1. TCP面向连接(如打电话要先拨号建立连接) ;UDP是无连接的,即发送数据之前
    不需
    要建立连接
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且
    按序到达;UDP尽最大努力交付,即不保证可靠交付,
  3. TCP面向字节流,实际上是TCP把数据看成一 连串无结构的字节流;UDP是面向报文的
    UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如
    IP电话,实时视频会议等)
    4.每一条TCP连接只能是点到点的;UDP支持一 对一,一对多,多对一和多对多的交互通信
  4. TCP首部开销20字节;UDP的首部开销小,只有8个字节
  5. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
端口号作用

​ 一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等

​ 这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。

​ 实际上是通过“IP地址+端口号"来区分不同的服务的。端口提供了一种访问通道,
服务器一般都是通过知名端口号来识别的。例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单 文件传送协议)服务器的UDP端口号都是69。

字节序

Little endian小端字节序:将低序字节存储在起始地址
Big endian 大端字节序:将高序字节存储在起始地址
网络字节序=大端字节序

Linux 提供的API简析

1.指定讲汉语" (连接协议)

int socket(int domain, int type, int protocol);

1.domain:

​ 指明所使用的协议族,通常为AF_INET ,表示互联网协议族( TCP/IP协议族) ;
​ ●AF_ INET IPv4因特网域
​ ●AF_INET6 IPv6因特网域
​ AF_UNIX Unix域
​ ●AF_ROUTE 路由套接字
​ ●AF_KEY密钥套接字
​ AF_UNSPEC 未指定

2.type参 数指定socket的类型:

●SOCK STREAM:

​ 流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
●SOCK DGRAM

​ 数据报套接字定义了一种无连接的服,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议 UDP。
●SOCK RAW
​ 允许程序使用低层协议,原始套接字允许对底层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

3.protocol

通常赋值"0”。
●日选择type类型对应的默认协议

​ ●IPPROTO TCP TCP传输协议

​ ●IPPROTO _UOP UDP传输协议

​ ●IPPROTO SCTP SCTP传输协议

​ ●IPPROTO TIPC TIPC传输协议*

2.地址准备好
bind()函数: IP号端口号与相应描述字赋值函数
#include <sy/types.h>	/ See NOTES”/
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
socklen_ t addrlen);

功能
用于绑定IP地址和端口号到socketfd
参数
sockfd
是一个socket描述符
addr
是一 个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同

//ipv4对应的是:
struct sockaddr{
	unisgned short as_ _family;			//协议族
	char sa_ data[14]; 					//IP+端口
};
//同等替换:
struct sockaddr_in {
	sa_family_t 		sin_ famQJy;	/* 协议族1/
	in_port_t			sin_ port;		/* 端口号"/
	struct in_addr	 	sin_ addr; 		/IP地址结构体*/
	unsigned char 		sin_ _zero[8];	/* 填充没有实际意义只是为跟sockaddr结构在内存中对齐这样两者才能相互转换*/
地址转换API
int inet_ aton(const char* straddr,struct in_ addr *addrp);/*把字符串形式的"192.168.1.123"转为网络能识别的格式*/
char* inet_ ntoa(struct in_ addr inaddr);/*把网络格式的ip地址转为字符串形式*/
3.监听
listen()函数:监听设置函数
#include <sys/types.h>/ See NOTES /
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能
  • 设置能处理的最大连接数,listen**()** 并未开始接受连线,只是设置sockectlisten模式,listen 函数只用于服务器端,服务器进程不知道要与谁连接,因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接, 然后响应该连接请求,并对它做出处理,一个服务进程可以同时处理多个客户进程的连接。主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数。

  • 内核为任何-个给定监听套接字维护两个队列:

    • 未完成连接队列,每个这样的SYU 报文段对应其中项:已由某个客户端发出并到达服务器,而服务器正在等待完成相应的TCP三次握手
      过程。这些套接字处于SYN_REVD状态:

    • 已完成连接队列,每个已完成TCP三次握手过程的客户端对应其中-项。 这些套接字处于ESTABLISHED状态

参数
  • sockfd
    sockfdsocket系统调用返回的服务器端socket描述符
  • backlog
    backlog指定在请求队列中允许的最大请求数
4.连接
accept()函数
#include <sys/types.h>				/* See NOTES */
#include' <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_ t *addrlen);
功能

●accept 函数由TCP 服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程被投入睡眠。

参数

●sockfd
sockfdsocket系统调用返回的服务器端socket描述符
●addr
​ 用来返回已连接的对端(客户端)的协议地址
​ ●addrled
​ 客户端地址长度

返回值

​ ● 该函数的返回值是一 个新的套接字描述符,返回值是表示已连接的套接字描述符,而第一 个参数是服务器监听套接字描述符。一个服务器通常仅
​ 仅创建一个监听套接字, 它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建一个已连接套接字(表示 TCP三次握
​ 手已完成,当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。

数据收发
4、字节流读取函数
/*在套接字通信中进行字节读取函数: read() , write()。与I/O中的读取函数略有区别,因为它们输入或输出的字节数比可能比请求的少。*/
1 ssize_t write(int fd, const void *buf,size_t nbytes);
2 ssize_t reld(int fd,void *buf,size_t nbyte);
3
4 /*说明
5 *函数均返回读或写的字节个数, 出错则返回-1
6 1*/
第一个将/* buf */中的/* nbytes */个字节写入到文件描述符/* fd */中,成功时返回写的字节数。第二个为从/* fd */中读取/* nbyte */个字节到/* buf */中,返回实际所读的字节数。详细应用说明参考使用/* read write */读写 socket (套节字)。
网络/* I/0 */还有一些函数例如: /*recv()/ send(),readv()/ writev(),recvmsg()/ sendmsg(),ecvfrom()/ sendto ()*/
数据收发常用第二套API
5、在TCP套接字上发送数据函数:有连接
1 ssize. _t send(int s,const void *msg,size_ t len,int flags);
2 //包含3要素: 套接字s,待发数据msg,数据长度len
3 //函数只能对处于连接状态的套接字使用, 参数s为已建立好连接的套接字描述
4 //符, 即accept函数的返回值
5 //参数msg指向存放待发送数据的缓冲区
6 //参数len为待发送数据的长度, 参数flags为控制选项, -般设置为0
6、在TCP套接字上接收数据函数:有连接
1 ssize. _t recv(int s,void *buf,size. _t len,int flags);
2 //包含3要素:套接字s,接收缓冲区buf,长度len
3 //函数recv%(参数s所指定的套接字描述符 (必须是面向连接的套接字)上接收
4 //数据并保存到参数buf所指定的缓冲区
5 //参数len则为缓冲区长度, 参数flags为控制选项, - 般设置为0
6.客户端的connect函数

connect()函数:客户机连接主机

1 #include <sy/types.h>			/* See NOTES */
2 #include <sys/socket.h>
3 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能

​ ●该函数用于绑定之后的client端(客户端),与服务器建立连接

参数

●sockfd
是目的服务器的sockect描述符
●addr
是服务器端的IP地址和端口号的地址结构指针
●addrlen
地址长度常被设置为sizeof(struct sockaddr)

返回值

●成功返回0,遇到错误时返回-1,并且errno中包含相应的错误码

字节序转换api
#include <netinet/in.h>
uint16 t htons(uint16 t host16bitvalue); //返回网络字节序的值
uint32 t htonl(uint32 t host32bitvalue);
//返回网络字节序的值
uint16 t ntohs(uint16 t net16bitvalue); //返回主机字节序的值uint32_ t
ntohl(uint32 t net32bitvalue); //返回主机字节序的值
h代表host, n代表net, s代表short (两个字节),代表long (4个字节),通过上面的4个函数
可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR ANY, INADDR ANY指定
地址让操作系统自己获取

第六章 嵌入式开发——玩转树莓派

修改树莓派系统配置,启用串口登录树莓派

  1. 覆盖SD卡根目录的’config.txt’文件。

    # For more options and information see
    # http://rpf.io/configtxt
    # Some settings may impact device functionality. See link above for details
    
    # uncomment if you get no picture on HDMI for a default "safe" mode
    #hdmi_safe=1
    
    # uncomment this if your display has a black border of unused pixels visible
    # and your display can output without overscan
    #disable_overscan=1
    
    # uncomment the following to adjust overscan. Use positive numbers if console
    # goes off screen, and negative if there is too much border
    #overscan_left=16
    #overscan_right=16
    #overscan_top=16
    #overscan_bottom=16
    
    # uncomment to force a console size. By default it will be display's size minus
    # overscan.
    #framebuffer_width=1280
    #framebuffer_height=720
    
    # uncomment if hdmi display is not detected and composite is being output
    #hdmi_force_hotplug=1
    
    # uncomment to force a specific HDMI mode (this will force VGA)
    #hdmi_group=1
    #hdmi_mode=1
    
    # uncomment to force a HDMI mode rather than DVI. This can make audio work in
    # DMT (computer monitor) modes
    #hdmi_drive=2
    
    # uncomment to increase signal to HDMI, if you have interference, blanking, or
    # no display
    #config_hdmi_boost=4
    
    # uncomment for composite PAL
    #sdtv_mode=2
    
    #uncomment to overclock the arm. 700 MHz is the default.
    #arm_freq=800
    
    # Uncomment some or all of these to enable the optional hardware interfaces
    #dtparam=i2c_arm=on
    #dtparam=i2s=on
    #dtparam=spi=on
    
    # Uncomment this to enable infrared communication.
    #dtoverlay=gpio-ir,gpio_pin=17
    #dtoverlay=gpio-ir-tx,gpio_pin=18
    
    # Additional overlays and parameters are documented /boot/overlays/README
    
    # Enable audio (loads snd_bcm2835)
    dtparam=audio=on
    
    [pi4]
    # Enable DRM VC4 V3D driver on top of the dispmanx display stack
    dtoverlay=vc4-fkms-v3d
    max_framebuffers=2
    
    [all]
    #dtoverlay=vc4-fkms-v3d
    dtoverlay=pi3-disable-bt		//停止蓝牙,解除对串口的使用
    start_x=1
    gpu_mem=128
    
  2. 然后再修改根目录的“cmdline.txt”,将里面的内容全部替换成以下内容,以防万一,先备份

    dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
    #console=serial0,115200 console=tty1 root=PARTUUID=ea7d04d6-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait quiet splash plymouth.ignore-serial-consoles
    
  3. 如果树莓派启动遇到以下问题…刷机吧…

    end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(179,XX)
    

修改树莓派登录密码

sudo passwd pi      #修改默认的用户名为pi的密码,按照提示重复输入两次新密码即可
sudo passwd root  	#修改root账户的初始密码
su root        	    #切换登陆到root账户
su pi               #切换到pi账户

让树莓派接入网

sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

nano 是文档编辑器

network={
		ssid="SMXY-WIFI"	/*注意:树莓派3B,WiFi的频率要设置成2.4GHZ,不要切换成5G的,不然检测不到*/
		#psk=“密码”
		key_mgmt=WPA-PSK
        		 #NONE
}

固定IP地址

指令:sudo nano /etc/rc.local

IP=$(hostname-I)||true
if["$IP"];then
printf "My IP address is 8s\n" "$ IP"
fi
ifconfig wlan0 192.168.43.123	//添加该行代码
exit 0

打开远程登录选项

sudo raspi-config
1、选择Interfacing Options

在这里插入图片描述

2、选择SSH

在这里插入图片描述

3、选择YES

在这里插入图片描述

vim的安装

sudo nano /etc/apt/sources.list

deb http://mirrors.tuna.tsinghua.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpi
#deb-src http://mirrors.tuna.tsinghuac.edu.cn/raspbian/raspbian/ stretch main contrib non-free rpi
sudo apt-get update	//更新源
    
sudo apt-get install vim

图形方式登录树莓派

Xrdp

sudo apt-get install xrdp

FileZila文件共享

主机:sftp://192.168.1.115
用户名:pi
密码:1

Linux 库概念及编程

  1. 静态库

    是在程序执行前就加入到目标程序中去了

    优点:

    1. 静态库北大报道应用程序中加载速度快
    2. 发布程序无需提供静态库,因为已经在app中,移植方便

    缺点:

    1. 链接时完整的拷贝至可执行文件中,被多次使用就用多分冗余拷贝。
    2. 更新、部署、发布麻烦
    3. 文件比较大
  2. 动态库

    是在程序执行时临时由目标去调用。

    优点:

    1. 链接时不复制,程序运行时由系统动态加载到没存,供程序调用,系统只加载一次,多个程序可以共用,节省内存。
    2. 程序升级简单,因为app里面没有库的源代码,升级之后只要库的名字不变,函数名以及参数不变,只是实现做了优化,就能加载成功。
    3. 文件小

    缺点:

    1. 运行慢

    2. 发布程序需要提供依赖的动态库

    动态函数库同共享函数库是一个东西(在linux上叫共享对象库, 文件后缀是.so ,windows上叫动态加载函数库,文件后缀是.dll)

    Linux中命名系统中共享库的规则

在这里插入图片描述

分文件编程案例

优点
  1. 功能责任划分
  2. 方便调试
  3. 主程序简洁
calcufuncs.c
int add(int a,int b){
        return a + b;
}
int sub(int a,int b){
        return a - b;
}
int mul(int a,int b){
        return a * b;
}
float div(int a,int b){
        return (float)a / b;
}
calcufuncs.h
int add(int a,int b);
int sub(int a,int b);
int mul(int a,int b);
float div(int a,int b);
calcumain.c
#include <stdio.h>
#include "calcufuncs.h"	/*优先搜索并导入当前目录存在的头文件*/

int main(){

        int a,b;

        while(1){
                printf("Please input first value\n");
                scanf("%d",&a);
                printf("Please input second value\n");
                scanf("%d",&b);

                printf("%d + %d = %d\n",a,b,add(a,b));
                printf("%d - %d = %d\n",a,b,sub(a,b));
                printf("%d * %d = %d\n",a,b,mul(a,b));
                printf("%d / %d = %g\n",a,b,div(a,b));

        }

        return 0;
}

库的制作

mv calculatorT.c ~将该文件移动到工作目录
cp ~/calculatorT.c .将该文件从工作目录移动到当前路径

(一)静态库

以以上三个文件为例:
calcufuncs.c
calcufuncs.h
calcumain.c
(1)命名规则

​ 静态库文件名的命名方式是 libxxx.a ,库名前加lib ,后缀用.axxx为静态库名。

(2)制作步骤
1. 先生成.o文件
gcc calcufuncs.c -c
2. 再将.o文件打包
#ar rcs 静态库的名字.a	原材料.o(.o文件)
ar rcs libcalcufunc.a calcufuncs.o

ar指令不会用:ar加Enter键可获取所有用法

(3)静态库的使用
1. 目录下已有这些文件
calcufuncs.c  calcufuncs.h  calcumain.c  libcalcu.a
2. 对主函数进行编译
gcc calcumain.c -lcalcu 	#-l + 库名(砍头去尾)  

​ 发现系统会报错
在这里插入图片描述

​ 解决办法:指定库的所在路径

gcc calcumain.c -lcalcu -L ./ -o mainProStatic
	 	意思是:在当前文件去链接calcu静态库
    #	-L告诉gcc编辑器从-L指定的路径去找静态库。默认从/usr/lib 或 /usr/local/lib找
    #	-o mainProStatic给编译文件起个名字

(二)动态库

(1)命名规则

​ 动态库的命名方式与静态库类似,前缀相同,为lib,后缀变为.so,所以为libmytime.so

(2)制作步骤
  1. 原材料:源代码.c 或者 .cpp

  2. 直接打包

gcc -shared -fpic calcufuncs.c -c -o libcalcu.so
-shared 指定生成动态库
-fpic 标准,fpic选项作用于编译阶段,在生成目标文件时就得使用该选项,以生成位置无关的代码



#### (3)动态库的使用

```powershell
gcc calcumain.c -lcalcu
也会出现上面的情况:找不到
应该:
gcc calcumain.c -lcalcu -L ./ -o mainProDyno

运行:

./mainProDyno

结果:发现找不到这个库

在这里插入图片描述

因为动态库是在程序执行时(临时)由目标去调用

解决方法:

/*将动态库拷贝到默认路径下*/
sudo cp libcalcu.so /usr/lib
/*再运行*/

在这里插入图片描述

完美解决!!!

(4)引用动态库并指定动态库的位置
#export LD_LIBRARY_PATH="动态库的路径名"
在终端命令输入:
export LD_LIBRARY_PATH="/home/pi/myCode/LinuxStockFile/test"
但该路径只是临时的,换一个窗口就不能用了(只要窗口不变就有效)
可以写个脚本快速执行搭建路径的指令
vi start.sh
    输入以下东西
		export LD_LIBRARY_PATH="/home/pi/myCode/LinuxStockFile/test"
		./mainProDyno
    然后 :wq
    
#给脚本添加权限
chmod +x start.sh
    
#运行
./start.sh
    

树莓派驱动继电器

gpio readall	//树莓派的所有io口

在这里插入图片描述

在这里插入图片描述

/*驱动一个继电器*/
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>

#define Relay 7

int main(){

        int cmd;

        if(wiringPiSetup() == -1)
        {
                printf("IO init failure!\n");
                return -1;
        }

        pinMode(Relay,OUTPUT);			//定义接口类型
        while(1){
                printf("please input 0 or 1:    0 - OFF , 1 - ON\n");
                scanf("%d",&cmd);		//输入指令

                if(cmd == 0)
                 digitalWrite(Relay,HIGH);
                        printf("you turn OFF the Relay!\n");
                }
                else if(cmd == 1)
                {
                        digitalWrite(Relay,LOW);
                        printf("you turn ON the Relay!\n");
                }
                else
                {
                        printf("input error!\nplease check your input value(just 0 or 1)\n");
                }
        }
        return 0;
}               
/*控制多个继电器*/
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>

#define Relay1 21
#define Relay2 22
#define Relay3 23
#define Relay4 24

void initRelay()
{
        pinMode(Relay1,OUTPUT);
        pinMode(Relay2,OUTPUT);
        pinMode(Relay3,OUTPUT);
        pinMode(Relay4,OUTPUT);

        digitalWrite(Relay1,HIGH);
        digitalWrite(Relay2,HIGH);
        digitalWrite(Relay3,HIGH);
        digitalWrite(Relay4,HIGH);
}

void controlRelay(int cmd)
{
        switch(cmd)
        {
                case 1:
                        digitalWrite(Relay1,LOW);
                        printf("you turn ON the Relay1!\n");
                        break;
                case 2:
                        digitalWrite(Relay2,LOW);
                        printf("you turn ON the Relay2!\n");
                        break;
                case 3:
                        digitalWrite(Relay3,LOW);
                        printf("you turn ON the Relay3!\n");
                        break;
                case 4:
                        digitalWrite(Relay4,LOW);
                        printf("you turn ON the Relay4!\n");
                        break;
                case 100:
                        digitalWrite(Relay1,LOW);
                        digitalWrite(Relay2,LOW);
                        digitalWrite(Relay3,LOW);
                        digitalWrite(Relay4,LOW);
                        printf("you turn ON all Relay!\n");
                        break;
                case -1:
                        digitalWrite(Relay1,HIGH);
                        printf("you turn OFF the Relay1!\n");
                        break;
                case -2:
                        digitalWrite(Relay2,HIGH);
                        printf("you turn OFF the Relay2!\n");
                        break;
                case -3:
                        digitalWrite(Relay3,HIGH);
                        printf("you turn OFF the Relay3!\n");
                        break;
                case -4:
                        digitalWrite(Relay4,HIGH);
                        printf("you turn OFF the Relay4!\n");
                        break;
                case -100:
                        digitalWrite(Relay1,HIGH);
 						digitalWrite(Relay1,HIGH);
                        digitalWrite(Relay2,HIGH);
                        digitalWrite(Relay3,HIGH);
                        digitalWrite(Relay4,HIGH);
                        printf("you turn OFF all Relay!\n");
                        break;
                default:
                        printf("input error!\nplease check your input\n");
                        break;
        }
}

int main(){

        int cmd;

        if(wiringPiSetup() == -1)
        {
                printf("IO init failure!\n");
                return -1;
        }

        initRelay();

        while(1)
        {
                printf("-----------------------\nplease input a int...\n");
                scanf("%d",&cmd);
                controlRelay(cmd);
        }
        return 0;
}

树莓派驱动超声波

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

#define Trig 4
#define Echo 5

void superIo_Init()
{
        pinMode(Trig,OUTPUT);
        pinMode(Echo,INPUT);
}

float disMeasure()
{
        struct timeval t1;
        struct timeval t2;

        long t_launch,t_receive;

        float dis;

        digitalWrite(Trig,LOW);
		delayMicroseconds(2);

        digitalWrite(Trig,HIGH);		//发出超声波脉冲 
        delayMicroseconds(10);
        digitalWrite(Trig,LOW);

        while(digitalRead(Echo) != 1);
        gettimeofday(&t1,NULL);	//获取当前时间 开始接收到返回信号的时候

        while(digitalRead(Echo) != 0);
        gettimeofday(&t2,NULL);//获取当前时间  最后接收到返回信号的时候

        t_launch = t1.tv_sec * 1000000 + t1.tv_usec;		//微秒级的时间
        t_receive = t2.tv_sec * 1000000 + t2.tv_usec;		//微秒级的时间

        dis = (float)(t_receive - t_launch)/1000000 * 34000 / 2;

        return dis;
}

int main(){

        float dis;
 		if(wiringPiSetup() == -1)
        {
                printf("setup wiringPi failed!\n");
                return -1;
        }

        superIo_Init();

        while(1)
        {
                dis = disMeasure();
                printf("distance = %gcm\n",dis);
                delay(1000);
        }

        return 0;
}

超声波控制继电器的闭合

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

#define Relay 21

#define Trig 4
#define Echo 5

void superIo_Init()
{
        pinMode(Trig,OUTPUT);
        pinMode(Echo,INPUT);
}

float disMeasure(void)
{
        struct timeval t1;
        struct timeval t2;

        long t_launch,t_receive;
long t_launch,t_receive;

        float dis;

        digitalWrite(Trig,LOW);
        delayMicroseconds(2);

        digitalWrite(Trig,HIGH);
        delayMicroseconds(10);
        digitalWrite(Trig,LOW);

        while(digitalRead(Echo) != 1);
        gettimeofday(&t1,NULL);

        while(digitalRead(Echo) != 0);
        gettimeofday(&t2,NULL);


        t_launch = t1.tv_sec * 1000000 + t1.tv_usec;
        t_receive = t2.tv_sec * 1000000 + t2.tv_usec;

        dis = (float)(t_receive - t_launch) / 1000000 * 34000 / 2;
		
		return dis;
}

int main(){

        int cmd;
        float dis = 0.0;

        if(wiringPiSetup() == -1)
        {
                printf("IO init failure!\n");
                return -1;
        }

        pinMode(Relay,OUTPUT);
        digitalWrite(Relay,HIGH);
        superIo_Init();

        while(1){
                dis = disMeasure();
                printf("dis = %g cm\n",dis);
                if(dis < 15.0)
                {
						digitalWrite(Relay,LOW);
                        printf("dis = %g cm,Relay Open!!\n",dis);
                }
                else
                {
                        digitalWrite(Relay,HIGH);
                        printf("dis = %g cm,Relay Close!!\n",dis);
                }
                delay(1000);
        }

        return 0;

}

语音模块实现语音识别功能

#include <wiringSerial.h>
#include <wiringPi.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

int main(){

        int fd;

        int nread;

        char cmd[128] = {'\0'};

        wiringPiSetup();

        fd = serialOpen("/dev/ttyAMA0",9600);

        while(1){

                nread = read(fd,cmd,sizeof(cmd));

                while(strlen(cmd) == 8){

                        if(strstr(cmd,"ON")){
                                printf("open  light\r\n");
                        }

                        if(strstr(cmd,"OFF")){
                                printf("close light\r\n");
                        }

                        memset(cmd,'\0',sizeof(cmd));

                }

        }

        return 0;
}

串口通讯协议概述

全双工:

​ 两个人同时互骂;

半双工:

​ 同一时间只能一个骂,一个听。

第七章 Linux内核

交叉编译

交叉编译是什么?为什么要交叉编译?

是什么=?
交叉编译是在一个平台上生成另一个平台上的可执行代码。我们再windows上面编写C51代码,并编译成可执行代码,如XX . hex, 是在C51上面运行,不是在windows, 上面运行
我们在ubuntu上面编写树莓派的代码,并编译成可执行代码,如a. out,是在树莓派上面运行,不是在ubuntu linux 上面运行

编译:是在一个平台上生成在该平台上的可执行代码

C51交叉编译的发生在keil(集成环境上面)
STM32

=为什么要交叉编译=:
平台上不允许或不能够安装我们所需要的编译器比如C51

​ 因为目的平台上的资源贫乏,无法运行我们]所需要编译器

*树莓派是不是就不需要交叉编译====*?
​ 错。也要.树莓派有时又是因为目的平台 还没有建立,连操作系统都没有,根本谈不上运行什么编译器。
​ 操作系统也是代码,也要编译!
​ 平台运行需要两样至少东西: bootloader (启动引导代码)以及操作系统核心

宿主机(host) : 编辑和编译程序的平台,-般是基于X86的PC机,通常也被称为主机。
目标机(target) : 用户开发的系统,通常都是非X86平台。host编译得到的可执行代码在target上运行。

交叉编译需要用到工具:

​ 交叉编译器

交叉编译工具链的安装

压缩包地址: https://github.com/raspberrypi/

解压

unzip tools-masters.zip
cd ~/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin

pwd获得路径

echo $PATH				//获取当前变量的值
2.1 临时有效

PATH 环境变量

CLC@Embed_Learn:~/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin$ export PATH = /usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/CLC/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bicn
2.2 永久有效

修改工作目录下的.bashrc隐藏文件,配置命合终端的

vi /home/CLC/ .bashrc

在文件最后一行加入:

export PATH = /usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/CLC/tools-master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
source home/CLC .bashrc		# 生效配置
2.3 查看结果
arm-linux-gnueabihf-gcc -v		# 查看版本号
显示:
gcc version 4.8.3 20140303 ........
arm-linux-gnueabihf-gcc xxx.c -o xxx		# 编译文件
scp test_2 pi@192.168.43.30:home/pi/		# 将编译文件拷贝到开发板上
2.4 带wiringPi库 的交叉编译如何进行
  1. 正常我们先要交叉编译wiringPi库,编译出的库适合树莓派,这时候交叉编译可执行程序的时候,链接库的格式也是正确的。

  2. 通过-I -L来指定

    因为链接的库的格式不对,是宿主机的平台,出现以下错误

    arm-linux- gnueabihf-gcc demo2.c -I /home/CLC/lessonPI/WiringPi/wiringPi - lwiringPi/home/CLC/lessonPI/tools- master/ arm- bcm2708/ gcc-linaro- arm- 1linux”gnue abihf-raspbian-x64/bin/ ../1ib/gcc/
    arm-linux- gnueabihf/4.8.3/../../../../arm-linux- gnueabihf/bin/1d: cannot find -1wiringPi collect2: error: 1d returned 1 exit status
    

    解决办法

    把树莓派的wiringpi库拿来用

    1、

    #下位机
    cd /usr/lib
    

[外链图片转存失败,源站可能有防```盗链机制,!建wiriPi库]议将(D:\MyDocumetQAPVStudy Note\嵌入式开发\wiringPi库.png)D:\MyDocument\Study Note\嵌入式开发\wiringPi库.png)]

2、

   # 下位机
	ls -l |grep wiringPi

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qtsFAXxo-1616205631600)(D:\MyDocument\Study Note\嵌入式开发\wiringPi库2.png)]

有‘->’的说明是软链接

3、

# 下位机
scp libwiringPi.so.2.50 CLC@192.168.43.52:/home/CLC
2.5 自己创建软链接
ln   -s   libwiringPi.so.2.50   libwiringPi.so
指令 参数   要被链接的文件			   定义的名字

参考链接: https://www.cnblogs.com/zhangna1998517/p/11347364.html.

概念:

  1. 软链接文件有类似于Windows的快捷方式。

  2. 在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息。

  3. 你选定的位置上生成一个文件的镜像,不会占用磁盘空间

    如何生成:

​ ln -s libwiringPi.so.2.50 libwiringPi. so
​ 指令 参数 要被链接的文件 软链接文件名字
硬链接: 它会在你选定的位置,上生成一个和源文件大小相同的文件

ln libwiringPi.so.2.50 libwiringPi.so

Ubuntu系统更新国内源

# 备份源列表
sudo cp /etc/apt/sources.list /etc/apt/sources.list_backup
# 打开sources.list文件
sudo nano /etc/apt/sources.list
# 编辑/etc/apt/sources.list文件, 在文件最前面添加阿里云镜像源:
#  阿里源
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
# 刷新列表
sudo apt-get update
#sudo apt-get upgrade
sudo apt-get install build-essential

虚拟机的三种网络连接模式

链接: https://www.cnblogs.com/linjiaxin/p/6476480.html.

树莓派内核开发

dsk@DSK:~/System/linux-rpi-4.19.y$ ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL=kernel7 make -j4 zImage modules dtbs
dsk@DSK:~/System/linux-rpi-4.19.y$ cp arch/arm/boot/dts/.*dtb* /home/dsk/data1/
dsk@DSK:~/System/linux-rpi-4.19.y$ cp arch/arm/boot/dts/overlays/.*dtb* /home/dsk/data1/overlays/
dsk@DSK:~/System/linux-rpi-4.19.y$ cp arch/arm/boot/dts/overlays/README /home/dsk/data1/overlays/

内核更改前

在这里插入图片描述

内核更改后

在这里插入图片描述

第八章 智能家居开发

1、结构体新玩法

#include <stdio.h>

struct Animal{
    char name[20];
    int sex;
    int age;
    
    void (*peat)();
    void (*pjump)();
};

void dogEat(){
    printf("狗吃屎\n");
}
void catEat(){
    printf("猫吃鱼\n");
}
void duckEat(){
    printf("鸭子吃米\n");
}

void dogjump(){
    printf("狗跳\n");
}
void catjump(){
    printf("猫跳\n");
}
void duckjump(){
    printf("鸭子跳\n");
}
int main(){
	
    struct Animal dog = {"大黄",1,4,dogEat(),dogjump()};
    struct Animal cat = {
        .age = 3,
        .peat = catEat(),
        .pjump = catjump()
    };
    //struct Animal duck;
    dog.peat;
    cat.peat;
    return 0;
}

2、工厂模式

3、main.c

#include "h_ControlDevices.h"

struct Device* findDeviceByName(char *name,struct Device *phead){

        struct Device *tmp = NULL;
        tmp = phead;

        if(tmp == NULL){
                return NULL;
        }else{
                while(tmp != NULL){
                        if(strcmp(tmp->dev_Name,name) == 0){
                                return tmp;
                        }
                        //tmp->dev_Init();
                        tmp = tmp->next;
                }
                return NULL;
        }

}
int main(int argc, char *argv[]) {

        int x;
        char name[32];

        if(wiringPiSetup() == -1){
                return -1;
        }

        struct Device *pDeviceHead = NULL;
        pDeviceHead = add_Bathroom_Light_To_Dev_Link(pDeviceHead);
        pDeviceHead = add_Second_Floor_Light_To_Dev_Link(pDeviceHead);
        pDeviceHead = add_Livingroom_Light_To_Dev_Link(pDeviceHead);
        pDeviceHead = add_Restaurant_Light_To_Dev_Link(pDeviceHead);
        pDeviceHead = add_Fire_Listen_To_Dev_Link(pDeviceHead);
         
        while(1){
                memset(name,'\0',sizeof(name));
                printf("====input name====\n");
                scanf("%s",name);
                struct Device *tmp = findDeviceByName(name,pDeviceHead);
                if(tmp == NULL){
                        printf("This is NULL!\n");
                }else{
                        tmp->dev_Init();
                        while(1){
                                printf("***input cmd***\n");
                                scanf("%d",&x);
                                if(x == 0)break;
                                tmp->dev_Change_Status(x);
                                //printf("Device Name:%s\n",tmp->dev_Name);
                                //printf("Device Pin_Num:%d\n",tmp->pin);
                        }
                }
        }
        return 0;
}

第九章 无刷电机

一、无刷电机的命名

​ 相对有刷电机,无刷电机的命名好理解很多,一般它由四个数字组成,例如2040无刷电机。这个数字仅代表电机的外形尺寸,2040表示直径为20mm,长度为40mm的电机。同理3650无刷电机表示此电机直径36mm,长度50mm。

​ 其实370有刷电机的大小和2530无刷电机一样,540电机的大小和3650无刷电机一样。

二、无刷电机的特点

  1. ​ 无刷电机的转速是严格按照KV值设定的,1000KV表示每一福特电机转速加快1000转。所以电压为5V时,1000kv的无刷电机转速5000rpm。
  2. ​ 在运行过程中,同样转速电机的扭力是靠电调输出的电流强度决定的,电流越大扭力越大。(理想状态下,我们“聪明”的无刷电调会不断“监测”我们的电机是否需要更大的扭力,同时也会保证扭力不过剩,以免浪费表情)

三、外转子与内转子无刷电机

外转子无刷电机

磁铁“包”在外面。而A、B、C电极在里边。

电机的扭力更大,但转速却上不来

在模型中,一般外转子无刷电机的KV值在2000以内,而内转子无刷电机则可以到8000-9000kv。

一般飞机上常见外转子无刷电机,而模型车和模型船一般都使用内转子无刷电机。

四、有感无刷和无感无刷

​ 刚才说了无刷电机的转速是靠交流电频率决定的,那么电调要想方设法弄明白目前电机的转速以及当前电机的状态。其实这对已经正常运转的电机来说很容易,但对于一个刚刚起步或者运行速度很慢的电机来说就显得比较麻烦了(很难较准确的测出电机转速的状态),所以无感无刷电机会在低速时线性不好甚至可能会颤抖,而起步的扭力也难以强过同等级有刷电机。

但是人们发现无论什么运行状态的无刷电机,它的霍尔效应都是明显的,所以通过霍尔效应电调可以很容易的知道无论高速还是低速电机的运行状态,从而解决了无感无刷电机的毛病!但就目前来看霍尔传感器并不是廉价货,所以有感无刷电机、电调的价格会比无感无刷贵上许多。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农菌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值