Linux系统编程回顾(一)

进入Linux世界
在这里插入图片描述


前言

裸机的误区:。


一、Linux系统编程

  linux的核心思想:linux中一切皆文件。

1 Linux基础知识

  对linux系统编程之前,需要熟悉linux编程的一些常规操作,以下内容主要针对基础小白,补充linux的一些基础知识点。

1.1 Linux用户

  linux中有个超级用户,为root,可操作和访问所有的文件。

  linux其他用户的创建和删除操作:创建用户,采用useradd ×××;查看用户,采用 cat /etc/passwd;删除用户,采用userdel ×××。linux也和Windows相类似,也有相应的用户组,关于用户组的操作,与用户操作相类似:添加用户组: groupadd ×××;查看用户组 :cat /etc/passwd;删除用户组:groupdel ×××。

1.2 Linux权限

  关于linux下文件权限的描述:定义所有者、所属组以及其他人对文件的读、写和执行的权限。linux权限表的含义如下图所示:

在这里插入图片描述

  修改linux文件权限的命令:chmod,在修改文件权限的时候,一般是将各类所有者的权限值(8进制值)累加进行赋值,如chmod 777 表示所有者、所属组以及所有的用户都有读、写、可执行的权限。

1.3 Linux路径

  在linux下查找文件,需要cd到文件所属的目录路径下,linux提供了两种路径,一种是绝对路径,一种是相对路径。

  绝对路径:绝对路径是从“/”目录开始的,“/”也称为根目录。

  相对路径:相对路径一般是从“.”开始的,或者“../../”开头的路径,或者路径前没有“/”。表示的是相对于用户“当前路径”下的目录位置。因此,退出目录的时候,可用命令 :cd ../或 cd ../../,这种方法比较高效。

  查看绝对路径命令:pwd

2 Linux文件 I/O操作

  I/O操作即一般的输入输出操作,为直接IO操作,包含了打开、创建、读、写操作等内容。注:打开的文件描述符一定要记得关闭。

2.1 open操作

  open函数的两个原型

int open(const char* Path, int oflags);
int open(const char* Path, int oflags, mode_t mode);

  其中,第1个参数Path表示:路径名或文件名,路径名为绝对路径;第2个参数 oflags 表示:打开文件所采取的的动作。第3个参数mode表示:设置创建文件的权限。

  打开文件的动作有:

  • O_RDONLY 文件只读
  • O_WRONLY 文件只写
  • O_RDWR 文件可读可写 (以上三个参数中必须选择其中一个)(以下是可选的)
  • O_APPEND 每次写操作都写入文件的末尾
  • O_CREAT 若指定文件不存在,则创建该文件
  • O_EXCL 若创建的文件已存在,则返回-1,并修改errno值
  • O_TRUNC 若文件存在,且以只写/读写方式打开,则清空文件全部内容
  • O_NOCTTY 若路径名指向终端设备,不把把该设备作为此进程的控制终端
  • O_NDELAY 非阻塞方式操作文件
  • O_NONBLOCK 若路径名指向FIFO/块文件、字符文件。则把文件的打开和打开成功后的I/O操作,都设置为非阻塞模式。

  使用open函数会返回一个文件句柄,文件句柄是文件的唯一识别符ID,对文件的操作必须从读取句柄开始。

  打开文件的例子:open.c

//标准输入输出头文件
#include <stdio.h>   

//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

main(){
	int fd;  //句柄
	char *leds = "/dev/leds";  led灯的设备节点
	char *test1 = "/bin/test1";
	char *test2 = "/bin/test2";
	
	if((fd = open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0){  以可读写、不把该程序作为控制终端、无延时模式打开
		printf("open %s failed!\n",leds);
	}
		printf("\n%s fd is %d\n",leds,fd);
		
	if((fd = open(test1,O_RDWR,0777))<0){
		printf("open %s failed!\n",test1);
	}
		printf("%s fd is %d\n",test1,fd);	
		
	if((fd = open(test2,O_RDWR|O_CREAT,0777))<0){
		printf("open %s failed!\n",test2);
	}
		printf("%s fd is %d\n",test2,fd);	
}

2.2 creat操作

  creat函数的原型

int creat(const char* Pathname, mode_t mode);

  其中,第1个参数Path表示:路径名或文件名,路径名为绝对路径;第2个参数mode表示:设置创建文件的权限。

  创建文件的例子:creat.c

//标准输入输出头文件
#include <stdio.h>

//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

main()
{
	int fd;
	//开发板中已经存在/dev/leds文件
	char *leds = "/dev/leds";
	//开发板中不存在的文件/bin/test1
	char *test1 = "/bin/test1";
	//开发板中不存在的文件/bin/test2
	char *test2 = "/bin/test2";
	//需要新建的文件/bin/test3
	char *test3 = "/bin/test3";
	
	//使用open函数打开文件
	if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0){
		printf("open %s failed\n",leds); 
	}
		printf("%s fd is %d\n",leds,fd);
	//使用open函数打开不存在的文件,不添加O_CREAT标识符,会报错
	if((fd = open(test1, O_RDWR))<0){
		printf("open %s failed\n",test1); 
	}
	//打开文件创建文件,添加标志位O_CREAT表示不存在这个文件则创建文件
		if((fd = open(test2, O_RDWR|O_CREAT,0777))<0){
		printf("open %s failed\n",test2); 
	}
		printf("%s fd is %d\n",test2,fd);
	
	fd = creat(test3,0777);
	if(fd = -1){
		printf("%s fd is %d\n",test3,fd);
	}
	else{
		printf("create %s is succeed\n",test3);
	}
}

2.3 write操作

  write函数的原型

ssize_t write(int fd, const void* buf, size_t count);

  其中,第1个参数fd表示:使用open函数打开文件,返回的句柄;第2个参数 *buf表示:写入的数据;第三个参数count表示:最多写入字节数。

  该函数的返回值:当返回值为-1时,表示出错;当返回值为其它值时,表示实际写入的字节数。

  写操作的例子:write.c

//标准输入输出头文件
#include <stdio.h>

//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

main()
{
	int fd;
	char *testwrite = "/bin/testwrite";
	ssize_t length_w;
	char buffer_write[] = "Hello Write Function!";

	if((fd = open(testwrite, O_RDWR|O_CREAT,0777))<0){
		printf("open %s failed\n",testwrite); 
	}
	
	//将buffer写入fd文件
	length_w = write(fd,buffer_write,strlen(buffer_write));
	if(length_w == -1)
	{
		perror("write");
	}
	else{
		printf("Write Function OK!\n");
	}
	close(fd);
}

2.4 read操作

  read函数的原型

ssize_t read(int fd, const void* buf, size_t len);

  其中,第1个参数fd表示:使用open函数打开文件,返回的句柄;第2个参数 *buf表示:读出的数据保存的位置;第三个参数count表示:每次最多读len个字节数。

  该函数的返回值:出错返回-1;执行成功后返回实际读取值。

  读操作的例子:read.c

//标准输入输出头文件
#include <stdio.h>

//文件操作函数头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define MAX_SIZE 1000

main(){
	int fd;
	ssize_t length_w,length_r = MAX_SIZE,ret;
	char *testwrite = "/bin/testwrite";
	char buffer_write[] = "Hello Write Function!";
	char buffer_read[MAX_SIZE];
	
	if((fd = open(testwrite,O_RDWR|O_CREAT,0777))<0){
		printf("open %s failed!\n",testwrite);
	}
	length_w = write(fd,buffer_write,strlen(buffer_write));
	if(length_w == -1){
		perror("write");
	}
	else{
		printf("Write Function OK!\n");
	}
	close(fd);  //先写再读
	
	if((fd = open(testwrite,O_RDWR|O_CREAT,0777))<0){
		printf("open %s failed!\n",testwrite);
	}
	if(ret = read(fd,buffer_read,length_r)){
		perror("read");
	}
	printf("Files Content is %s \n",buffer_read);
	close(fd);
}

3 字符设备控制

  首要需要明确一点,什么是字符设备。字符设备指的是只能按一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据按先后设备。常见的字符设备有鼠标、键盘、串口、led等,在此处涉及的字符设备主要为led灯、蜂鸣器、数模转换灯设备。

  每一个字符设备在/dev目录下都有一个设备节点,可通过设备节点来使用驱动程序操作字符设备。

3.1 main函数传参

  main函数作为应用程序的入口,完整定义为:

int main (int argc, char **argv)

  其中,argc表示参数的个数,**argv表示存储输入字符的数组。argv[0] 表示程序名称,argv[1] - argv[n]表示输入的参数。

  当不传入参数的时候,main函数定义为:

int main(void)

  main函数传参的例子:argvc.c

#include<stdio.h> 
#include<string.h>

//argument count变元计数
//argument value变元值
int main(int argc,char *argv[])
{
	int i,j;
	i = atoi(argv[1]);
	j = atoi(argv[2]);
	
	printf("the Program name is %s\n",argv[0]);
	
	printf("The command line has %d argument:\n",argc-1);
	
	printf("%d,%d\n",i,j);
	
	return 0;
}

3.2 led灯控制

  控制led之前,需要补充一些知识点:led灯的设备节点在/dev目录下。以及LED灯的硬件原理:

在这里插入图片描述

  KP_C0L0和VDD50_EN网络高电平,三极管导通,led小灯就亮,低电平,则断路,小灯就灭。

  对于硬件IO口的操作,linux专门设计了高效的ioctl函数,ioctl函数是设备驱动程序中对设备的 I/O 通道进行管理,即对设备的一些特性进行控制。函数原型为:

int ioctl(int fd, int request, int cmd)

  fd含义同前;request表示led对应的IO口;cmd表示对IO口进行了什么样的操作。该函数的返回值为0时,表示成功;返回值为-1时,表示出错。

  测试小灯实验:leds.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define LED_NUM 2
#define LED_C 2
//cmd为0,则灭,为1,则亮;
//io为0则是靠近蜂鸣器的小灯,为1则靠近独立按键的小灯
int main(int argc,char *argv[])
{
	int fd,led_num,led_c;
	char *leds = "/dev/leds";

	led_num = LED_NUM;
	led_c = LED_C;
	
	printf("argv1 is cmd;argv2 is io \n"); 
	//对传入的参数进行判断,超出范围直接退出
	if (atoi(argv[1]) >= led_c) {         字符串转换成整型数
		printf("argv1 is 0 or 1)");
		exit(1);
	}
	if (atoi(argv[2]) >= led_num) {
		printf("argv2 is 0 or 1)");
		exit(1);
	}
	//使用ioctl函数将参数传入内核
	if((fd = open(leds, O_RDWR|O_NOCTTY|O_NDELAY))<0)
		printf("open %s failed\n",leds);   
	else{
			ioctl(fd,atoi(argv[1]),atoi(argv[2]));
			printf("ioctl %s success\n",leds);
		}
	close(fd);
	
	return(1);
}

3.3 蜂鸣器控制

  蜂鸣器的设备节点也在/dev目录下。以及蜂鸣器BeeP的硬件原理:

在这里插入图片描述

  若网络MOTOR_PWM为高电平,则导通,蜂鸣器响。

  测试蜂鸣器实验:buzzertest.c

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

#define BUZZER_C 2
int main(int argc,char *argv[]){
	char *buzzer_ctl = "/dev/buzzer_ctl";
	int fd,ret,buzzer_c;
	
	buzzer_c = BUZZER_C;
	
	if(atoi(argv[1]) >= buzzer_c ){
		printf("argv[1] is 0 or 1\n");
		exit(1);
	}
	
	if((fd = open(buzzer_ctl,O_RDWR|O_NOCTTY|O_NDELAY))<0){
		printf("open %s failed\n",buzzer_ctl);
		exit(1);
	}
	
	ret = ioctl(fd,atoi(argv[1]),atoi(argv[2]));
	close(fd);
	
	return 0;
}

3.4 ADC数模转换控制

  数模的设备节点也在/dev目录下。数模转换器的原理其实是华东变阻器,滑动变阻器最大阻值为R13,1和2之间的电阻R12,电压Vadc=R12*VDD1V8_EXT/R13,1.8V对应的是10k欧姆,对应的寄存器数值为0xffff,0V 对应的是0 欧姆,对应的寄存器数值为0x0。

在这里插入图片描述

  若网络MOTOR_PWM为高电平,则导通,蜂鸣器响。

  测试ADC实验:ADC.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdint.h>
#include <termios.h>
//#include <android/log.h>
//#include <sys/ioctl.h>

int main(void)
{
	int fd;
	char *adc = "/dev/adc";
	char buffer[512];
	int len=0, r=0;
	
	memset(buffer,0,sizeof(buffer));  buffer初始化
		printf("adc ready!\n");
	
	if((fd = open(adc, O_RDWR|O_NOCTTY|O_NDELAY))<0)
		printf("open adc err!\n");
	else{
		printf("open adc success!\n");
		
		len=read(fd,buffer,10);	
		
		if(len == 0)
			printf("return null\n");
		else{
			r = atoi(buffer);
			r = (int)(r*10000/4095);	//Datas  transition to Res
			printf("res value is %d\n",r);
		}			
	}
}

4 串口编程

4.1 串口编程的概念

  串口通信是指一次只传一个数据位,虽然通信时串口有8位或9位,但在物理层面传输,仍然是以单个bit方式传输。串口一般指的是标准的RS232接口。关于RS232接口,有A,B,C三种,其中最广泛使用的是RS-232C。RS232的针脚分为25针和8针。

  关于流控的概念:分为软件流控制和硬件流控制。其中软件流控制。

  linux串口编程的流程为:开始 → 打开串口 → 初始化串口 → 发送和接收数据 → 关闭。

  (1)打开串口,一般使用open函数,打开之后返回句柄;

  (2)初始化串口,配置波特率,数据位,校验位等;

  (3)发送和接收数据,使用read和write函数;

  (4)关闭,close函数。

4.2 开机启动(Hello world)

  打开vi编辑器,使用命令vi 、etc/init.d/rcS 打开文件系统的启动文件,在文件的最后一行,添加“/bin/hello world &” 。

4.3 打开串口

  在4412开发板中,设备节点使用的是ttySAC*系列,即ttySAC 0, ttySAC 1,ttySAC 2,ttySAC 3。精英版靠近耳机和麦克风的串口对应的设备节点为“ttySAC3”

  打开、关闭串口的例程:uartopen.c

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

void main(){
	int fd;
	char *uart3 = "/dev/ttySAC3";
	
	if((fd = open(uart3,O_RDWR|O_CREAT,0777))<0){
		printf("open %s failed!\n",uart3);
	}
	else{
		printf("open %s is success!\n",uart3);
	}
	
	close(fd);
}

4.4 串口初始化

  使用source insight打开内核源码,串口的初始化最终要将参数传递到内核中,搜索termios.h文件。打开“arch\arm\include\asm”目录下“termioss.h”头文件。

  termio结构体的定义:

  tcflag_t c_iflag:输入模式标志

  tcflag_t c_oflag:输出模式标志

  tcflag_t c_cflag:控制模式标志

  tcflag_t c_lflag:本地模式标志

  cc_tc_line:line discipline

1.串口初始化函数
(1)tcgetattr:用来读取当前串口参考值,一般用于先确认该串口是够能够配置,做检测用。
头文件:# include <termios.h> 和 # include <unistd.h>
函数原型:

int tcgetattr(int fd, struct termios * termios_p)

  参数1表示文件句柄,参数2 termios_p是结构体。

(2)波特率相关的函数

函数cfsetispeed和cfsetospeed用于修改串口的波特率。函数cfgetispeed和cfgetospeed可用于获取当前的波特率。头文件处于 # include <termios.h> 和 # include <unistd.h>中。

  • cfsetispeed函数原型:
int cfsetispeed (struct termios *termios_p, speed_t speed);

  参数1 *termios_p是结构体,参数2:speed 波特率,常用B2400,B4800,B9600,B115200,B460800。运行成功返回0,运行失败则返回-1。

  • cfsetospeed函数原型:
int cfsetospeed (struct termios *termios_p, speed_t speed);

  同上。

获取波特率的函数:

  • cfgetispeed函数原型:
speed_t cfgetispeed(const struct termios *termios_p)

  该函数用于读取当前串口输入的波特率,返回值为speed_t。

  • cfgetospeed函数原型:
speed_t cfgetospeed(const struct termios *termios_p)

  该函数用于读取当前串口输出的波特率,返回值为speed_t。

(3)tcflush:用于清空串口中没有完成的输入或输出数据。在接收或发送数据时,串口寄存器会缓存数据,这个函数用于清除这些数据。

函数原型:
int tcflush(int fd, int queue _selector)
参数1:fd是open返回的文件句柄;
参数2:控制tcflush的操作。

  控制tcflush的操作:

  • TCIFUSH :清除正收到的数据,且不会读取出来;
  • TCOFLUSH :清除正写入的数据,且不会发送至终端;
  • TCIOFLUSH :清除所有正在发生的 I/O数据

(4)tcsetattr:设置参数的函数

函数原型:
int tcsetattr (int fd, int optional_actions, const struct termios *termios_p);
参数1:fd,句柄
参数2:optinal_actions 是参数生效的时间;
参数3*termios_p在旧的参数基础上修改后的参数,(一般在初始化最后使用这个函数)。

  optional_actions操作种类:

  • TCSANOW :不等数据传输完毕就立即改变属性;
  • TCSADRAID :等待所有数据传输结束才改变属性;
  • TCSAFLUSH :清空输入输出缓冲区才能改变属性。

2.串口初始化流程

在这里插入图片描述

  串口初始化代码:

//定义一个初始化函数
int set_opt(int fd, int nSpeed, int nBits, char nEvent, int nStop);
//定义结构体 newtio 和 oldtio
struct termios newtio, oldtio;
//判断串口是否能够配置
if(tcgetattr(fd,&oldtio)!=0)
	{ 
	  perror("Setup Serial 1");  //保存原先串口设置
	  return -1;     //调试成功,返回0,失败,返回-1
	}
//将newtio清零,设置标志位c_cflag
bzero(&newtio, sizeof(newtio));
newtio.c_cflag 1 = CLLCAL | CREAD ; 本地连接|接收使能
newtio.c_cflag & = ~ CSIZE; 数据位的位掩码,通过位掩码的方式激活这两个选项

//设置停止位
switch(nBits)
{
	case 7:
		newtio.c_cflag 1 = CS7;  (7个数据位)
	break;
	case 8:
		newtio.c_cflag 1 = CS8;  (8个数据位)
	break;
}

//设置奇偶校验位
switch(nEvent)
{
	case 'O':  (使能校验)
		newtio.c_cflag 1 = PARENB;  校验位使能
		newtio.c_cflag 1 = PAROOD;  使用奇校验而不使用偶校验
		newtio.c_iflag 1 = (INPCK | ISTRIP)   奇偶校验使能|除去奇偶校验位
		break;
	case 'E': (使能偶校验)
		newtio.c_iflag 1 = (INPCK | ISTRIP);
		newtio.c_cflag 1 = PARENB;
		newtio.c_cflag &= ~PARODD;   先取反再做与运算
		break;
	case 'N':  
		newtio.c_cflag &= ~PARENB;
		break;
}

//设置波特率(cfsetispeed 和 cfsetospeed)
switch( nSpeed )
	{
	case 2400:
		cfsetispeed(&newtio, B2400);  输入速度
		cfsetospeed(&newtio, B2400);  输出速度
		break;
	case 4800:
		cfsetispeed(&newtio, B4800);
		cfsetospeed(&newtio, B4800);
		break;
	case 9600:
		cfsetispeed(&newtio, B9600);
		cfsetospeed(&newtio, B9600);
		break;
	case 115200:
		cfsetispeed(&newtio, B115200);
		cfsetospeed(&newtio, B115200);
		break;
	case 460800:
		cfsetispeed(&newtio, B460800);
		cfsetospeed(&newtio, B460800);
		break;
	default:
		cfsetispeed(&newtio, B9600);
		cfsetospeed(&newtio, B9600);
		break;
	}
//设置停止位
if( nStop == 1 )
	newtio.c_cflag &=  ~CSTOPB;
else if ( nStop == 2 )
	newtio.c_cflag |=  CSTOPB;

//设置最少字符和等待时间
newtio.c_cc[VTIME] = 0;  指定了读取每个字符的等待时间
newtio.c_cc[VMIN] = 0;  指定最少读取的字符数

//刷清输入缓存
tcflush(fd, TCIFLUSH);

//激活配置
if((tcsetattr(fd, TCSANON, &, newtio))! 0)  改变的配置立即生效
{
	perror("com set error");
	return -1;
}

4.5 串口发送

  使用write函数即可实现,三个参数分别是句柄,传输的buffer以及传输的长度。

  串口发送例程代码:uartwrite.c

#include <stdio.h>   //标准输入输出定义
#include <string.h>  //字符串函数定义
#include <unistd.h>  //标准函数unix定义
#include <sys/types.h>
#include <sys/stat.h>
#include <fcnt1.h>  //文件控制定义
#include <termios>  //PPSIX终端控制定义
#include <errno.h>  //错误号定义

//实现串口打印hello world十次
//采用靠近声卡旁的串口
//初始化函数
int set_opt(int, int, int , char, int)  //试用前先声明
void main()
{
	int fd, wr_static, i = 10;
	char *uart 3 = "/dev/ttySAC3";  ttySAC代表开发板上的串口
	char * buffer = "hello world !\n";
	printf("\r\n itop4412 uart3 writetest start \r\n");

//打开串口,可读写,不将该设备作为作为此进程的控制终端,非阻塞操作
	if((fd = open(uart3, O_RDWR|O_NOCTTY|O_NDELAY))<0){
		printf("open %s is failed",uart3);
	}
	else{
		printf("open %s is success\n",uart3);
		set_opt(fd, 115200, 8, 'N', 1);  设置串口的参数
		while(i--)
		{
			wr_static = write(fd,buffer, strlen(buffer));
			if(wr_static<0)
				printf("write failed\n");
			else{
				printf("wr_static is %d\n",wr_static);
			}
			sleep(1);
		}
	}
	close(fd);
}

  编译例程的操作步骤:

  • 编译成可执行文件
  • 开发板接两个串口,一个是控制台的超级终端,一个是靠近声卡的串口
  • 电脑端开启串口助手,设置与代码中相一致的参数
  • 开机,挂载U盘,运行可执行文件,在串口助手终端可看到打印信息,10次hello world,说明串口输出成功。

  若电脑端只有1个串口,编译例程的操作步骤又如下:

  • 在超级终端下,将uartwrite拷贝到/bin目录下,然后修改权限 chmod 777
  • 修改开机启动的脚本文件,添加“/bin/uartwrite &”,保存退出,关掉开发板
  • 将串口线接到耳机旁的串口座,打开串口助手

4.6 串口接收

  使用read函数即可实现,在之前的 I/O 操作已有介绍。

  串口接收例程代码:uartread.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>

int set_opt(int,int,int,char,int);
//"/dev/ttySAC3"是con2,靠近耳机接口的串口
void main()
{
	int fd,nByte;
	char *uart3 = "/dev/ttySAC3";
	char buffer[512];   定义一个字符串数组,接收数据
	char *uart_out = "please input\r\n";   
	memset(buffer, 0, sizeof(buffer));   buffer初始化
	if((fd = open(uart3, O_RDWR|O_NOCTTY))<0)
		printf("open %s is failed",uart3);
	else{
		set_opt(fd, 115200, 8, 'N', 1);   初始化串口
		write(fd,uart_out, strlen(uart_out));
		//循环接收串口发送过来的数据,并发送出去
		while(1){
			while((nByte = read(fd, buffer, 512))>0){
				buffer[nByte+1] = '\0';	 表示字符串结束		
				write(fd,buffer,strlen(buffer));   计算buffer中字符个数
				memset(buffer, 0, strlen(buffer));  清空buffer中数据
				nByte = 0;
			}
		}
	}
}

  编译例程的操作步骤:

  1. 编译成可执行文件
  2. 只用一个串口,所有设置,保存方法同写文件相同,只是文件的名字不同
  3. 电脑端开启串口助手,设置与代码中相一致的参数
  4. 开机,挂载U盘,运行可执行文件,在串口助手终端可看到“please input”,输入“hello world”,点击发送,返回信息“helloread”,说明串口接收成功。

5 网络编程

5.1 TFTP服务器搭建和测试

  为实现上位机与开发板在线传输文件,在Ubuntu上搭建一个服务器,通过网络传输文件到开发板。

(1)Ubuntu 服务端搭建

  TFTP(在线传输协议),专为小文件传输。首先 ping baidu.com,利用 control + c 暂停命令,可得 ping 的结果,然后下载安装:

sudo apt-get install xinetd
sudo apt-get install tftp tftpd

  然后建立TFTP的配置文件,使用命令: vi /etc/xinetd.d/tftp 建立文件:

service tftp
{
	socket_type = dgram
	protocol  = udp
	wait  = yes
	user  = root
	server  = /usr/sbin /in.tftpd
	server_args  = -s/var/tftpboot/
	disable  = no
	per_source  = 11
	cps = 100 2
	flags  = IPv4
}

其中server_args设置的/var/tftpboot目录是tftp服务器的目录,TFTP客户端就是从这个目录获取服务器上的文件,使用 mkdir /var/tftpboot 建立tftp服务器的目录。然后设置方位权限为777,输入重启命令:

sudo /etc/init.d/xinetd restart

(2)服务器测试

  • 在/var/tftpboot目录下新建文件test,输入…
  • 启动另一个终端,输入tftp 127.0.0.1
  • 输入get test,获取到test 文件
  • 输入q退出tftp
  • ls查询该文件是否存在当前目录下

5.2 TFTP服务器与开发板的文件传输

(1)设置同一网段IP地址
首先需要补充一个知识点,关于同一网段。两个网络IP是否在同一网段的计算方法:

两个IP分别和相应的子网掩码做 “与” 运算,得到的结果相同,则表明在同一网段。

注:进行与运算需要先转换为二进制。比如如下计算过程:
Ubuntu 系统:
IP :11010011 01000111 00001100 10011001
MASK :11111111 11111111 11111111 11111110
Result :11010011 01000111 00001100 10011000
Hex : 211 71 12 152
开发板:
IP :11000000 10101000 00000001 11100110
MASK :11111111 11111111 11111111 00000000
Result :11000000 10101000 00000001 00000000
Hex : 192 168 1 0
根据计算可知,两个IP不在同一网段,因此开发板需要重新修改IP地址。编辑IP地址的脚本文件在文件系统的 etc/eth0-setting文件下,

(2)Ping通TFTP服务器
Ubuntu上服务器的IP地址是 211.71.12.153
(3)从服务器获取文件

tftp -g -l test -r test 211.71.12.153

其中test是文件名,可以是文件包,也可以是编译后的可执行文件。

5.3 NFS网络文件系统

NFS的全称为 Network File System,即网络文件系统,主要应用于linux系统之间资源的共享。

   本质思想是:linux A路径下,share文件夹,linux B可利用挂载机制,将linux A系统的share文件夹挂到linux B中,从而实现资源的共享。

设置的原则是:PC上Ubuntu的ip和iTop4412上的ip在同一网段。

   搭建NFS服务器的步骤:

(1) 选择Ubuntu作为主机;
(2)Ubuntu上安装NFS服务器:

apt-get install nfs-kernel-server

(3)配置:

vi /etc/exports  添加:
/home/ topeet/linux/*(rw,sync,no_root_squash)

其中:
*:表示允许所有的网络端访问;
rw:表示可读写权限;
sync:表示资料同步写入内存和硬盘;
no_root_squash:是Ubuntu nfs客户端分享目录使用者的权限,若客户端使用的是root用户,那么对于该目录共享而言,该客户端就具有root权限。
(4)重启portmap服务器:

/etc/init.d/portmap restart

(5)重启nfs:

/etc/init.d/nfs_kernel_server restart

   NFS共享目录的使用步骤:

(1)查看开发板ip:192.168.1.103
(2)查看NFS服务器ip:192.168.1.104
(3)ping通网络:ping 192.168.1.104 -c 3
(4)在开发板/mnt 目录下,新建 “mkdir nfs”,作为开发板NFS同步目录:
(5)“mount -t nfs -o nolock 192.168.1.104 : /home/minilinux/minilinux /mnt/nfs ” 挂载nfs服务器
(6)在虚拟机系统上, /home/minilinux/minilinux (NFS服务器目录)使用命令 “vim hello” 新建文档
(7)在开发板上查看到相应的信息
(8)开发板和虚拟机完成同步

   注:在经开发板内烧写内核镜像时,USB_fastboot_tool中不能放两个内核镜像,每次都要执行所有的烧写命令,以防开发板不能启动。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值