【LINUX系统编程】嵌入式LINUX系统编程学习手记

一.使用BUSYBOX

make menuconfig 进入BUSYBOX界面
make 编译生成2进制文件
make install 将2进制文件安装到system文件夹下

二.最小系统的其他配置

创建dev etc lib mnt proc sys tmp var文件夹
etc文件夹
eth0-setting:IP MASK GATEWAY DNS MAC修改权限 chmod 755
新建Init.d 文件夹:新建ifconfig-eth0文件 …修改权限 chmod 755
新建rcS文件 …修改权限 chmod 755
新建passwd文件 …修改权限 chmod 755
新建profile 文件 …修改权限 chmod 755
新建rc.d 文件夹:新建init.d 文件夹
新建netd 文件 …修改权限 chmod 755
lib文件夹
拷贝动态链接库到lib目录

var文件夹
新建 lib lock log run tmp

三.编译烧写最小系统

1.编译

make_ext4fs -s -l 314572800 -a root -L linux system.img system

参数介绍
make_ext4fs用于Android平台上制作ext4文件系统的镜像。用法举例:
make_ext4fs -l 512M -s -a system system.ext4img system
之后再使用simg2img制作镜像。

有一个问题是,使用这样制作的镜像,system分区文件的权限都是预定的,即使先修改system目录文件权限后在制作镜像,烧入设备后,其权限仍未改变。关键问题在make_ext4fs工具,在制作ext4fs时更改了权限,其依据为system/core/private/android_ilesystem_config.h所定义的权限。

查询make_ext4fs的参数含义,可以了解到这一点。

-l 512M"是分区大小,i9100的system分区是512M;
-s就是生成ext4的S模式制作;
“-a system”,是指这个img用于android系统,挂载点是/system,使用这个参数,make_ext4fs会根据private/android_filesystem_config.h里定义好的权限来给文件夹里的所有文件重新设置权限,如果你刷机以后发现有文件权限不对,可以手工修改android_filesystem_config.h来添加权限,重新编译make_ext4fs,也可以不使用 “-a system”参数,这样就会使用文件的默认权限。
如果不使用-a参数,则可。


制作ext4文件系统需要使用make_ext4fs命令,例如:
#make_ext4fs -s -l 314572800 -a root -L linux ./rootfs_qt.img ./root
执行之后即会将root文件打包成 rootfs_qt.img 文件系统镜像。
下载:make_ext4fs。

关于各参数的含义
命令行下执行make_ext4fs即可看到使用方式:
root@DingMZ:~# make_ext4fs
Expected filename after options
make_ext4fs [ -l ] [ -j ] [ -b <block_size> ]
[ -g ] [ -i ] [ -I ]
[ -L

-s 就是生成ext4的S模式制作;
-l 314572800 是分区大小;
-a root 是指这个img用于Linux系统(若为-a system即表示为android系统,挂载点即是/system。使用这个参数,make_ext4fs会根据private/android_filesystem_config.h里定义好的权限来给文件夹里的所有文件重新设置权限,如果你刷机以后发现有文件权限不对,可以手工修改android_filesystem_config.h来添加权限,重新编译make_ext4fs,也可以不使用 “-a system”参数,这样就会使用文件的默认权限)。
./rootfs_qt.img 表示在当前目录下生成镜像文件。
./root 指定源路径。

2.烧写

程序编译

arm-none-linux-gnueabi-gcc -o read read.c -static

四.Linux 用户 用户组 权限

五.文件IO

这里的文件操作函数就API层面上与FATFS非常类似

LINUX系统常用文件操作

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

1.文件打开操作以及实验

open 的两个原型。
int open(const char *path, int oflags);
int open(const char *path, int oflags,mode_t mode);
第一个参数 path 表示:路径名或者文件名。路径名为绝对路径名,例如开发板中的 led
驱动的设备几点/dev/leds。
第二个参数 oflags 表示:打开文件所采取的动作。

O_RDONLY 文件只读
O_WRONLY 文件只写
O_RDWR 文件可读可写
下面是可以任意选择的。
O_APPEND 每次写操作都写入文件的末尾
O_CREAT 如果指定文件不存在,则创建这个文件
O_EXCL 如果要创建的文件已存在,则返回 -1,并且修改 errno 的值
O_TRUNC 如果文件存在,并且以只写/读写方式打开,则清空文件全部内容
O_NOCTTY 如果路径名指向终端设备,不要把这个设备用作控制终端。
O_NONBLOCK 如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O
第三个参数 mode 表示:设置创建文件的权限。
_IRUSR,S_IWUSER,S_IXUSR,S_IRGRP,S_IWGRP,S_IXGRP,S_IROTH,S_IWOTH,S_IXOTH.
其中 R:读,W:写,X:执行,USR:文件所属的用户,GRP:文件所属的组,OTH:其
他用户。

打开文件实验
#include <stdio.h>

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

main(){
int fd;
char *leds = “/dev/leds”;
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);	

}

文件创建操作以及实验
int creat(const char *pathname,mode_t mode)


#include <stdio.h>
 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
main()
{
	int fd;
	//exist file
	char *leds="/dev/leds";
	//not exist file
	char *test1="/bin/test1";
	char *test2="/bin/test2";
	//file to create
	char *test3="/bin/test3";	
	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))<0)
	{
		printf("open %s failed \n",test1);
	}
	printf("\n%s fd is %d\n",test1,fd);
	if((fd=open(test2,O_RDWR|O_CREAT,0777))<0)
	{
		printf("open %s failed \n",test2);
	}
	printf("\n%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 success\n",test3);
	}
}

2.文件读写操作
原形为:ssize_t write(int fd,const void buf,size_t count)
参数
buf,需要写入的数据
参数count,将参数*buf中最多count个字节写入文件中
返回-1写入失败,正常情况返回写入的字节数

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

#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);
	}
	
	length_w=write(fd,buffer_write,strlen(buffer_write));
	if(length_w==-1)
	{
		perror("write");
	}
	else
	{
		printf("Write Function OK!\n");
	}
	close(fd);
}



经测试文件打开运行均正常
六.字符控制设备

1.LED小灯


硬件IO操作

LINUX IO操作函数

 在头文件<unistd.h>
int ioctl(int fd,int request,int cmd)

Request 表示对哪一个IO口进行操作
cmd表示进行怎样的操作

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
 
#define LED_NUM 2
#define LED_C 2
//cmd:0 off 1 on
 
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\n");
		exit(1);
	}
	if(atoi(argv[2])>=led_num)
	{
		printf("argv2 is 0 or 1\n");
		exit(1);
	}
	//use ioctl
	if((fd=open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0)
	{
		printf("open %s failes\n",leds);
	}
	else
	{
		ioctl(fd,atoi(argv[1]),atoi(argv[2]));
		printf("ioctl %s success\n",leds);
	}
	close(fd);
	return 0;
}



2.蜂鸣器

蜂鸣器与LED操作类似

所有简单IO设备系统级操作操作都非常类似

包括判断 转换 打开设备 传参 关闭设备


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
 
#define BUZZER_C 2
//cmd:0 off 1 on
 
int main(int argc,char *argv[])
{
	int fd,buzzer_c;
	char *buzzer="/dev/buzzer_ctl";
 
	buzzer_c=BUZZER_C;
	
	printf("argv is cmd \n");
	if(atoi(argv[1])>=buzzer_c)
	{
		printf("argv is 0 or 1\n");
		exit(1);
	}
	//use ioctl
	if((fd=open(buzzer,O_RDWR|O_NOCTTY|O_NDELAY))<0)
	{
		printf("open %s failes\n",buzzer);
	}
	else
	{
		ioctl(fd,atoi(argv[1]),atoi(argv[2]));
		printf("ioctl %s success\n",buzzer);
	}
	close(fd);
	return 0;
}



LINUX系统编程实战1(通过延时函数让4412蜂鸣器唱歌)

之前学单片机的时候干过这样一件事,通过PWM波让STM32唱歌通过调节音频和延时来实现,今天刚学了LINUX系统编程正好来演练一下。因为找不到歌谱,所以只能暂时使用CSDN上找到的一个。
程序如下,其实和之前基本一样,就是改了一下底层代码。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
 
#define BUZZER_C 2
//cmd:0 off 1 on
int  fd;
//响一次
 void beepsound(void)
 {	
	 ioctl(fd,1,0);	 	 
 }
 //停止响一次
  void beepsoundNo(void)
 {	
	 ioctl(fd,0,0);	 	 
 }
 
 //以一定的频率响一次
 void Sound(int frq)
{
	int time;
	if(frq != 1000)
	{
		time = 500000/frq;
		beepsound();
		usleep(time);
		beepsoundNo();
		usleep(time);
	}else
		usleep(1000);
}



void playmusic(void)
{
	//              低7  1   2   3   4   5   6   7  高1 高2 高3 高4 高5 不发音
	int  tone[] = {247,262,294,330,349,392,440,294,523,587,659,698,784,1000};//音频数据表
	//红尘情歌
	int  music[]={5,5,6,8,7,6,5,6,13,13,//音调
                5,5,6,8,7,6,5,3,13,13,
                2,2,3,5,3,5,6,3,2,1,
                6,6,5,6,5,3,6,5,13,13,

                5,5,6,8,7,6,5,6,13,13,
                5,5,6,8,7,6,5,3,13,13,
                2,2,3,5,3,5,6,3,2,1,
                6,6,5,6,5,3,6,1,	

                13,8,9,10,10,9,8,10,9,8,6,
                13,6,8,9,9,8,6,9,8,6,5,
                13,2,3,5,5,3,5,5,6,8,7,6,
                6,10,9,9,8,6,5,6,8};	
	int  time[] = {2,4,2,2,2,2,2,8,4, 4, //时间
                2,4,2,2,2,2,2,8,4, 4, 
                2,4,2,4,2,2,4,2,2,8,
                2,4,2,2,2,2,2,8,4 ,4, 

                2,4,2,2,2,2,2,8,4, 4, 
                2,4,2,2,2,2,2,8,4, 4, 
                2,4,2,4,2,2,4,2,2,8,
                2,4,2,2,2,2,2,8,

                4, 2,2,2, 4, 2,2,2, 2,2,8,
                4, 2,2,2,4,2,2,2,2,2,8,
                4, 2,2,2,4,2,2,5,2,6,2,4,
                2,2 ,2,4,2,4,2,2,12};	

	int  yanshi;
	int  i,e;
	yanshi = 10;
	for(i=0;i<sizeof(music)/sizeof(music[0]);i++)
	{
		for(e=0;e<(time[i])*tone[music[i]]/yanshi;e++)
		{
			Sound(tone[music[i]]);
		}	
	}

	
}

最后好像效果不佳,还是用PWM效果好一点。等我学会LINUX驱动后在回来重写一下。

int main(void)
{

int melody[] = {50, 50, 50, 50, 200, 200, 200, 400, 400, 500, 500, 500};
int buzzer_c;
char *buzzer="/dev/buzzer_ctl";

buzzer_c=BUZZER_C;

//use ioctl
if((fd=open(buzzer,O_RDWR|O_NOCTTY|O_NDELAY))<0)
{
	printf("open %s failes\n",buzzer);
}
else
{

	printf("ioctl %s success\n",buzzer);
	playmusic();
	
	

	
}
close(fd);
return 0;

}

七.LINUX 串口编程

串口对应设备结点:

Tty

串口关闭与打开操作

串口编程流程
打开串口:一般使用open函数,打开后返回句柄,这个句柄就可以提供给发送和接收函数使用

    初始化串口:需要设置波特率、校验位、数据位、停止位等一系列的参数,初始化比较麻烦,要知道如何进行研制和配置

   发送和接收数据:属于字符设备,可以使用read函数和write函数实现

   关闭:使用函数close即可关闭串口

串口初始化参数介绍
常用的:串口号、波特率、数据位、停止位、校验位、流控

struct termio {
unsigned short c_iflag; /* input mode flags /
unsigned short c_oflag; /
output mode flags /
unsigned short c_cflag; /
control mode flags /
unsigned short c_lflag; /
local mode flags /
unsigned char c_line; /
line discipline /
unsigned char c_cc[NCC]; /
control characters */
}

串口初始化常用函数
头文件<termios.h><unistd.h>

函数tcgetattr
读取当前参数值,一般用于先确认该串口是否能配置,做检测用
int tcgetattr(int fd,struct termios *termios_p)
先创建一个termios结构体来存储旧的参数

    波特率相关函数

         函数cfsetisspeed和cdsetosspeed用于修改串口的波特率,函数cfgetisspeed cfgetosspeed用于获取当前的波特率

        设置函数
        int cfsetisspeed(struct termios *termios_p,speed_t speed);
        speed:波特率,常用的B2400,B4800,B9600,B115200等,
        int cdsetosspeed(struct termios *termios_p,speed_t speed);

        获取函数
        speed_t cfgetisspeed(const struct termios *termios_p)
        speed_t cfgetosspeed(const struct termios *termios_p)



      函数tcflush

        清空缓存寄存器,清空串口中没有完成的输入或者输出数据,在接收和发送时

串口寄存器会缓存数据
int tcflush(int fd,int queue_selector)
fd是open返回的文件句柄
queue_selector控制tcflush的操作
三个常用的数值,TCIFLUSH清除正受到的数据,且不会读取出来;TCOFLUSH清除正写入的数据,且不会发送至终端;TCIOFLUSH清除所有正在发生的IO数据

    函数tcsetattr
   参数设置函数
         int tcsetattr(int fd,int optional_actions,const struct termios*termios_p)
         optional_actions:参数生效时间
        三个常用的值:TCSANOW,不等数据传输完毕就立即改变属性;TCSADRAIN,等待所有数据传输结束才改变属性;TCSAFLUSH,清空输入输出缓冲区才改变属性
        termios_p在旧的参数基础上修改后的参数

LINUX系统编程实战(LINUX串口编程)
串口编程算是学完了,对于LINUX系统编程我也已经有了一个初步的概念
本来以为LINUX没了库函数 得要一个位一个位的去设置和单片机没有库函数一样,现在发现自己错了
LINUX作为一个中介,很好的将底层函数与上层系统分割开来,所有的操作都是围绕着文件展开的,在完成驱动的封装后我们所需要做的仅仅就是对于文件的读取与写入。
怪不到之前听说LINUX中一切都是文件。
观察LINUX的文件进入 dev文件夹 所有的硬件都被打包成文件

这次来搞个串口的编程

实现开机后打印10个HELLOWORLD
然后进入死循环轮询串口:收到指令后将收到的内容返回


#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);

void main(){
	int fd,wr_static,i=10,nByte;
	char *uart3 = "/dev/ttySAC3";
	char *buffer0 = "hello world!\n";
	char *buffer1 = "你发送的消息为:\n";
    char buffer[512];	
	char *uart_out = "please input\r\n";
	memset(buffer, 0, sizeof(buffer));
    if((fd=open(uart3,O_RDWR|O_CREAT,0777))<0)
	{
		printf("failed");

	}
	else{
		printf("success");
        set_opt(fd, 115200, 8, 'N', 1); 
		//发送十次数据
		while(i--)
		{
			wr_static = write(fd,buffer0, strlen(buffer0));
            //向设备结点写入10次数据
			if(wr_static<0)
				printf("write failed\n");
			else{
				printf("wr_static is %d\n",wr_static);
			}
			sleep(1);
		}
        write(fd,uart_out, strlen(uart_out));
   		while(1){
				//如果串口读到数据
		   //提醒输入
		   
			while((nByte = read(fd, buffer, 512))>0){
				buffer[nByte+1] = '\0';//在数据后加结束符
 				write(fd,buffer1,strlen(buffer1));//把接收到的发送回去            				
				write(fd,buffer,strlen(buffer));//把接收到的发送回去
				memset(buffer, 0, strlen(buffer));//将接受buffer清空
				nByte = 0;
			}
	
	
		
		
		
		
	             }
       close(fd);

	}

   

}


//串口通用初始化函数
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
	struct termios newtio,oldtio;//定义结构体newtio和oldtio
	//测试串口能否运行并将原串口的数据取到oldtio
	if  ( tcgetattr( fd,&oldtio)  !=  0) { 
		perror("SetupSerial 1");
		return -1;
	}
	//将newio清零和设置c_cflag
	bzero( &newtio, sizeof( newtio ) );
	newtio.c_cflag  |=  CLOCAL | CREAD;//使能接收和忽略控制线
	newtio.c_cflag &= ~CSIZE;
	//设置数据位
	switch( nBits )
	{
	case 7:
		newtio.c_cflag |= CS7;
		break;
	case 8:
		newtio.c_cflag |= CS8;
		break;
	}
	//设置校验位
	switch( nEvent )
	{
		//偶校验
	case 'O':
		newtio.c_cflag |= PARENB;//使能奇偶校验
		newtio.c_cflag |= PARODD;//偶校验
		newtio.c_iflag |= (INPCK | ISTRIP);//输入校验并忽略第八位
		break;
	case 'E': 
		newtio.c_iflag |= (INPCK | ISTRIP);
		newtio.c_cflag |= PARENB;
		newtio.c_cflag &= ~PARODD;//取消偶校验(置零偶校验位),开启奇校验
		break;
	case 'N':  
		newtio.c_cflag &= ~PARENB;//不进行奇偶校验
		break;
	}
	//设置波特率
	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;//读取最小字符数为0
	tcflush(fd,TCIFLUSH);//清空缓冲区
	//进行初始化设置
	if((tcsetattr(fd,TCSANOW,&newtio))!=0)
	{
		perror("com set error");
		return -1;
	}
//	printf("set done!\n\r");
	return 0;
}

实现上没有任何困难,底层已经完全被打包好了,调用API函数即可

八.延时函数

LINUX内核延时
Linux 中常用的函数有以下函数 sleep、usleep、ndelay、udelay、mdelay 等
Linux 系统编程下用到的延时函数在头文件“#include <unistd.h>”中,包括函数 sleep、
usleep。
Linux 内核中用到的延时函数在“#include <linux/delay.h>”中,包括函数 ndelay、
udelay、mdelay。

sleep
秒延时
usleep
udelay
微秒延时
mdelay
毫秒延时

注意sleep在系统编程用
delay 只在内核用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

与光同程

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

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

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

打赏作者

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

抵扣说明:

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

余额充值