目录
4.1交叉编译hello
#include <stdio.h>
/* 执行命令: ./hello weidongshan
* argc = 2
* argv[0] = ./hello
* argv[1] = weidongshan
*/
int main(int argc, char **argv)
{
if (argc >= 2)
printf("Hello, %s!\n", argv[1]);
else
printf("Hello, world!\n");
return 0;
}
//argc是参数个数,hello argv[0]
//./hello weidongshan aa
//hello weidongshan
//./hello "weidongshan aa"
//./hello weidongshan aa
arm-buildroot-linux-gnueabinf-gcc -o hello hello.c
stdio.h 头文件在系统目录(工具链)和指定目录
printf 函数在库中,也分系统目录和指定目录
···user 1.app 2.lib.c printf
···3.kernel fs/driver open read
···硬件
4.2GCC
4.2.1GCC编译过程
预处理 ,查找头文件,展开宏---.i, ---编译--- 汇编语言.s ---汇编--- 机器语言.o--- 链接,多个文件组装在一起
语法错误在编译中发现
4.2.2gcc编译选项
gcc -o -c main.o mian.c
gcc -o -c sub.o sub.c
gcc -o test main.o sub.o
//先预处理,编译汇编,但是不链接
<stdio.h>是去工具链指定的路径查找
" sub.h"是在当前目录下查找,改成<sub.h>是 gcc -c -o main.c -I ./,指定头文件目录,或者加入到工具链的路径
静态库,找的到库位置
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
ar crs libsub.a sub.o sub2.o sub3.o(可以使用多个.o生成静态库)
gcc -o test main.o libsub.a (如果.a不在当前目录下,需要指定它的绝对或相对路径)
动态库,找不到
gcc -c -o main.o main.c
gcc -c -o sub.o sub.c
gcc -shared -o libsub.so sub.o sub2.o sub3.o(可以使用多个.o生成动态库)
gcc -o test main.o -lsub -L /libsub.so/所在目录/
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
./test
静态库的在静态阶段链接到了程序中,动态库在程序运行的时候系统动态加载到内存中供程序调用
库 指定的lib目录, 或者-L
运行时 so 指定lib路径,或者LD_LIBRARY_PATH
Makefile
Makefile规则
只对修改的文件相关的重编译
比较时间 gcc -o test a.o b.o 如果test时间早于a.o b.o ,说明被修改
1.依赖文件比目标文件新 2.目标文件不存在 执行命令
//依赖文件:目标文件
//TAB命令
makefile
test:a.o b.o
gcc -o test a.o b.o
a.o:a.c
gcc -o -c a.o a.c
b.o:b.c
gcc -o -c b.o b.c
//makefile文件包括这些内容,执行make命令就可以了
Makefile语法
通配符 %.o
test:a.o b.o c.o
gcc -o test $^//所有的依赖
%.o:%.c
gcc -o -c $@目标文件 $<第一个依赖文件
假想目标
make [目标] 若无目标,默认第一个目标
test:a.o b.o c.o
gcc -o test $^//所有的依赖
%.o:%.c
gcc -o -c $@目标文件 $<第一个依赖文件
clean:
rm *.o test
.PHONY: clean
若文件中有clean同名文件,则不能执行make命令
所以采用假想目标 .PHONY:clean,就不会判断clean是否存在
变量
简单变量(即时变量)
A:=XXX //A的值立刻决定
B=XXX //B的值到使用时才决定
:= //即时变量
= //延时变量
?= //延时变量,如果是第一次定义才起效,如果在前面该变量已定义则忽略这句话
+= //附加:他是即时变量还是延时变量取决于前面的定义
A:=$(C)
B=$(C)
C=abc
D=100ASK
D?=SU
all:
@echo A=$(A)
@echo B=$(B)
@echo D=$(D)
C+=123
//A=
//B=abc 123
//D=100ASK
函数
$(foreach var,list,text)对list 中的每一个元素,取出来赋给var,然后把var改为text所描述 的形式。
A=a b c
B=$(foreach f,$(A),.$(f).o)
all:
@echo$(B)
//a.o b.o c.o
$(filter pattern...,text)在text中取出符合pattern格式的值
$(filter-out pattern...,text)在text中取出不符合pattern格式的值
A= a b c d/
B=$(filter %/,$(A))
C=$(filter-out %/,$(A))
all:
@echo$(B)
@echo$(C)
//d/
//a b c
$(wildcard pattern) pattern所列出的文件是否存在,把存在的文件都列出来。
$(wildcard *.c)
$(patsubst pattern,replacement,$(var)从列表中取出每一个值如符合pattern则替换成replacement
file=a.c b.c c.c abc
file1=$(patsubat %.c ,%.d,$(file))
all:
@echo$(file1)
//a.d b.d c.d
实例
c.c需要依赖头文件c.h
gcc -M c.c打印出依赖
gcc -M -MF c.d c.c//把依赖写入c.d
gcc -c -c c.o c.c -MD -MF c.d//编译c.c,把依赖写入c.d
objs=a.c b.c c.c
dep_files:=$(patsubst %,.%.d,$(objs))
dep_files:=$(wildcard$(dep_files)
CFLAGS=-Werror -Iinclude//把警告都当成错误,把头文件放到include
test:a.o b.o c.o
gcc -o test $^
@echo dep_files=$(dep_files)//a..d b.o.d c.o.d都生成.d文件,就不用单独把从c.d写个命令
ifneq($(dep_files),)
include$(dep_files)
endif
%.o:%.c
gcc $(CFLASS) -o -c $@ $< -MD -MF .$@.d
clean:
rm *.o test
disclean:
rm$(dep_files)
.PHONY: clean
文件
标准IO
系统调用IO
比如这个fread第一次进入系统内核,存入buf,之后都不进入内核,直接从buf里读数据
open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include<unistd.h>
/*./open 1.txt
argc=2
argv[0]=./open
argv[1]=1.txt
*/
int main (int argc,char **argv)
{
int fd;
if(argc!=2){
printf("Usage: %s <file>\n",argv[0]);
return -1;
}
fd=open(argv[1],O_RDWR);
if(fd<0){
printf("can not open file :%s\n",argv[1]);
printf("errro is :%d\n",errno);
printf("err:%s\n",strerror(errno));//把错误类型打印出来
perror("open");
}
while(1){
sleep(10);
}
close(fd);
return 0;
}
0号标准输入,1标准输出,3 错误信息,fd=3要打开的文件 ./open open.c &
open的权限RDWR 文件权限只读无法打开
fd=open(argv[1],O_RDWR|O_CREAT|O_TRUNC);
mode&~umask
umask002 book
对于已经存在的文件,无法修改文件,重新创建的文件可以修改文件
write
./write 1.txt abc 2024 913
for(i=2;i<argc;i++){
int len=write(fd,argv[i],strlen(argv[i]));
if(len!=strlen(argv[i]))
{
perror("write");
break;
}
write(fd,"\r\n",2);
}
read
while (1)
{
int len=read(fd,buf,sizeof(buf));
if(len<0){
perror("read");
close(fd);
return -1;
}else if(len==0){
break;
}
else{
buf[len]='\0';
printf("%s",buf);
}
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include<unistd.h>
/*逐行处理
readline
process_line
写入新文件
./process score.csv result.csv
argc=3
argv[0]=./process
argv[1]=score.csv
argv[2]=result.csv
*/
//返回值n一行数据个数,-1表示读到尾部或者出错
static int read_line(int fd,unsigned char *buf){
//循环的读入字符,读到0X0d 0x0a
unsigned char c;
int len;
int i=0;
int err=0;
while (1)
{
len=read(fd,&c,1);
if(len<=0){
err=-1;
break;
}else{
if(c!='\n'&&c!='\r'){
buf[i]=c;
i++;
}else{
//碰到回车换行
err=0;
break;
}
}
}
buf[i]='\0';
if(err&&(i==0)){
//读到了尾部,并且一个数据都没有读进来
return -1;
}else{
//读到尾部了,之前读到了数据
return i;
}
}
int process_data(unsigned char *data_buf,unsigned char *result_buf){
//第一行不做处理
//之后的处理数据,求和评价
char name[100];
int score[3];
int sum;
char *levels[]={"A+","A","B"};
int level;
if(data_buf[0]==0xef)//UTF-8文件,前3个字符是0XEF 0XBB 0XBF
{
strcpy(result_buf,data_buf);
}else{
sscanf(data_buf,"%[^,],%d,%d,%d,",name,&score[0],&score[1],&score[2]);
//printf("result:%s,%d,%d,%d\n\r",name,score[0],score[1],score[2]);
sum=score[0]+score[1]+score[2];
if(sum>=170)
level=0;
else if(sum>240)
level=1;
else
level=2;
sprintf(result_buf,"%s,%d,%d,%d,%d,%s",name,score[0],score[1],score[2],sum,levels[level]);
// printf("result:%s",result_buf);
}
}
int main (int argc,char **argv)
{
int fd_data,fd_result;
int i;
unsigned char data_buf[1000];
unsigned char result_buf[1000];
if(argc!=3){
printf("Usage: %s <score><result> ...\n",argv[0]);
return -1;
}
fd_data=open(argv[1],O_RDWR|O_CREAT,0664);
if(fd_data<0){
printf("can not open file :%s\n",argv[1]);
perror("open");
return -1;
}else{
printf("data fd=%d\n",fd_data);
}
fd_result=open(argv[2],O_RDWR|O_CREAT,0664);
if(fd_result<0){
printf("can not create file :%s\n",argv[2]);
perror("create");
return -1;
}else{
printf("result fd=%d\n",fd_result);
}
while (1)
{
//从数据文件读取一行
int len =read_line(fd_data,data_buf);
if(len==-1){
break;
}
//处理数据
if(len!=0){
process_data(data_buf,result_buf);
write(fd_result,result_buf,strlen(result_buf));
write(fd_result,"\r\n",2);
}
//写入结果文件
/// write_data(fd_result,result_buf);
}
close(fd_result);
close(fd_data);
return 0;
}
系统调用
long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
struct open_flags op;
int fd = build_open_flags(flags, mode, &op);
struct filename *tmp;
if (fd)
return fd;
tmp = getname(filename);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
fd = get_unused_fd_flags(flags);//未使用的句柄
if (fd >= 0) {
struct file *f = do_filp_open(dfd, tmp, &op);//打开文件得到file结构体
if (IS_ERR(f)) {
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f);
fd_install(fd, f);当前进程下记录下来
}
}
putname(tmp);
return fd;
}
file-> f_pos
void fd_install(unsigned int fd, struct file *file)
{
__fd_install(current->files, fd, file);
}
current->task struct->files->fdtable fdt->fd数组->file->f_pos
int fd=open(argv[1],O_RDONLY);
int fd2=open(argv[1],O_RDONLY);
int fd3=dup(fd);
fd=3;
fd2=4;
fd3=5;
但是fd3和fd对应同一个file结构体,当read(fd,buf,1)f_pos从0-1
read(fd3,buf,1)会从1-2
dup2
1文件重定向到3文件对应的1.txt,hello world输入到1.txt
字符串
0.........x_wide-1
.......
0........x_wide-1 y行
0....x
(y*x_wide+x)*BPP/8,bpp是一个像素用多少位表示,除以8是字节数
(x,y)像素起始地址=fb_base+(xres*bpp/8)*y + x*bpp/8
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
unsigned char *pen_8 = fb_base+y*line_width+x*pixel_width;
API
open
ioctl获取分辨率和bpp
mmap framebuffer是驱动管理,得映射到应用空间
字符
ANSI编码,选择不同的字符集对应不一样
unicode,一对一,包含ascii码,最大字节0xFFFFFF
网络通信
服务器被动的响应请求
客户端 主动发起请求
TCP可靠的 UDP不可靠
服务器根据源端口来区分同一个IP下的两个连接 客户端
fd=socket() fd=socket
bind(自己的ip,端口,)把fd和IP端口绑定起来
listen(启动监测数据
accept(接受一条连接 connect建立连接
send recv
串口
UART
UART硬件
用途:打印信息,外接各种模块
使用串口
1.波特率 2.格式:数据位,停止位,校验位,流控
怎么发送A,0X41 0b01000001
双方约定波特率,每一位占据的时间,假设1s
数据+校验位+停止位
115200.8n1
波特率115200,八位数据位,一位停止位
开始位,数据位,停止位 10位
每一位需要1/115200s,每秒传输 115200/10=11520byte
TTY设备节点的区别
/dev/ttyS0串口 /dev/ttySAC0 /dev/tty当前终端 /dev/tty0前台终端 /dev/tty1 /dev/console
TTY设备驱动程序,输入输出设备 terminal终端 Console 控制器 UART串口
TTY驱动程序的框架
在Linux系统中,操作设备的统一接口,open,ioctl,read,write
UART在ioctl之上封装了很多函数,主要是设置行规程
*open
*设置行规程(结构体),比如波特率,数据位,停止位,校验位,RAW模式(APP应用处理数据等,一有数据就返回
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <stdlib.h>
/* 设置set_opt(fd,115200,8,'N',1) */
int set_opt(int fd,int nSpeed, int nBits, char nEvent, int nStop)
{
struct termios newtio,oldtio;
//清空
if ( tcgetattr( fd,&oldtio) != 0) {
perror("SetupSerial 1");
return -1;
}
bzero( &newtio, sizeof( newtio ) );
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~CSIZE;
newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /*Input*/
newtio.c_oflag &= ~OPOST; /*Output*/
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;
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[VMIN] = 1; /* 读数据时的最小字节数: 没读到这些数据我就不返回! */
newtio.c_cc[VTIME] = 0; /* 等待第1个数据的时间:
* 比如VMIN设为10表示至少读到10个数据才返回,
* 但是没有数据总不能一直等吧? 可以设置VTIME(单位是10秒)
* 假设VTIME=1,表示:
* 10秒内一个数据都没有的话就返回
* 如果10秒内至少读到了1个字节,那就继续等待,完全读到VMIN个数据再返回
*/
tcflush(fd,TCIFLUSH);
if((tcsetattr(fd,TCSANOW,&newtio))!=0)
{
perror("com set error");
return -1;
}
//printf("set done!\n");
return 0;
}
int open_port(char *com)
{
int fd;
//fd = open(com, O_RDWR|O_NOCTTY|O_NDELAY);
fd = open(com, O_RDWR|O_NOCTTY);
if (-1 == fd){
return(-1);
}
if(fcntl(fd, F_SETFL, 0)<0) /* 设置串口为阻塞状态*/
{
printf("fcntl failed!\n");
return -1;
}
return fd;
}
/*
* ./serial_send_recv <dev>
*/
int main(int argc, char **argv)
{
int fd;
int iRet;
char c;
/* 1. open */
/* 2. setup
* 115200,8N1
* RAW mode
* return data immediately
*/
/* 3. write and read */
if (argc != 2)
{
printf("Usage: \n");
printf("%s </dev/ttySAC1 or other>\n", argv[0]);
return -1;
}
fd = open_port(argv[1]);
if (fd < 0)
{
printf("open %s err!\n", argv[1]);
return -1;
}
iRet = set_opt(fd, 115200, 8, 'N', 1);
if (iRet)
{
printf("set port err!\n");
return -1;
}
printf("Enter a char: ");
while (1)
{
scanf("%c", &c);
iRet = write(fd, &c, 1);
iRet = read(fd, &c, 1);
if (iRet == 1)
printf("get: %02x %c\n", c, c);
else
printf("can not get data\n");
}
return 0;
}
*read/write
I2C
写操作
start操作-设备地址7bit -方向0表示写1表示读-回应-数据8bit-回应-数据8bit-回应-P结束
SDA 从高到低 -开始 从低到高-结束 切换数据 保持数据稳定 从设备存在拉低信号,回应
SCL 保持高电平 保持高电平 低电平时 高电平
主设备和从设备都驱动时,采用三极管,保证电路安全
对SDA 前8CLK主设备,9CLK从设备,ACK=0
对SCL 前9CLK主设备,10从设备,SCL=0从设备处理一下别的事情
SMBus协议,系统管理总线
SMBus是I2C的一个子集
重要结构体
I2C_adapter .nr第几条I2C总线 .xfer传输函数
i2ctools
访问i2c_dev.c i2c_gpio.c 访问i2c——devices