天天看点

Linux之字符设备控制Linux之字符设备控制

Linux之字符设备控制

一、入口 main 函数的参数

虽然所有人都学习过 C 语言,但是对 C 语言中的 main 函数的用法并不是很清楚。由于后面的实验需要用到这部分知识,这里就占用一个小节,先简单介绍一下 main 函数。

1、main 函数简介

main 函数作为应用程序的入口,在头文件“#include<stdio.h> ”中。

main 函数需要传参数的时候完整的定义为

int main(int argc,char **argv)
//参数 argc,表示参数的个数
//参数**argv,存储输入字符的数组
//argv[0]表示程序名称
//argv[1]——argv[n]输入的参数
           

不传参数的时候定义为

函数 main 的返回值为类型为 int,用于判断程序执行成功还是失败

2、main 函数例程

编写简单的 argvc.c 文件测试 main 函数。

代码如下所示,将输入的参数第一个和第二个转换成 int 类型,赋值给 i 和 j,最后输出打印。其中 argv[0]为程序名称,这里就是后面要编译的目标文件“argvc”。

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

编译成可执行的文件argvc,拷贝到 U 盘,启动最小Linux系统,插入 U 盘,加载 U 盘,运行程序如下。

Linux之字符设备控制Linux之字符设备控制

如上图所示,程序成功运行,打印:

the Program name is ./mnt/udisk/argvc。因为运行的程序是“./mnt/udisk/argvc”,这是第一个参数

The command line has 2 argument:输入的个数有两个。

10,11。输入的参数是 10 和 11,对应 argv[1]和 argv[2]。

二、字符类 led 灯

1、led灯简介

在前面介绍 open 函数的时候,已经提到过如何打开字符类设备,获得句柄的方法和一般文件都是一样。

由于涉及到硬件知识,这里简单介绍一下硬件原理,如下图所示,led 小灯的硬件原理很简单。

Linux之字符设备控制Linux之字符设备控制

如上图所示,给 KP_COL0 和 VDD50_EN 网络高电平,三极管 L9014 就会导通,电源 VSYS就会将电压加到电阻 R 和 led 小灯上,小灯就会亮。

给 KP_COL0 和 VDD50_EN 网络低电平,三极管 L9014 就会截止,形成断路,小灯灭。

在前面介绍过,如果要给文件进行写操作,那么使用的是 write 函数。对于 led 小灯的操作,使用写函数,理论上也是可以的。但是对于 IO 口(这里的 IO 口指的是硬件上的 IO 口,不是指 IO 文件)的操作,Linux 专门设计了一个高效的函数 ioctl。

这个函数在头文件#include<unistd.h>中。

int ioctl( int fd, int request, int cmd);
//参数 fd,函数 open 返回的句柄。
//参数 request 和参数 cmd,由内核驱动决定具体操作,例如 request 可以代表那个 IO 口,
//cmd 代表对 IO 进行什么样的操作,也可以反过来。具体的含义由驱动工程师在驱动中 switch决定。
//返回值:返回 0 成功;返回-1,出错。
           

2、小灯测试例程

#include <stdio.h>
//通过 main 参数传过来的参数是 char 字符格式的,如果要传递给 ioctl 函数,需要用到数值转化函数 atoi,添加了头文件#include <stdlib.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

//由于小灯的数量和命令都是 2,所以对小灯数量和操作数进行宏定义
#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);
}
           

将编译成的可执行文件 open,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,使用命令“./mnt/udisk/leds 0 0”运行程序如下。可以看到其中一个灯灭了。

Linux之字符设备控制Linux之字符设备控制

三、字符类 Buzzer 蜂鸣器

1、蜂鸣器简介

Linux之字符设备控制Linux之字符设备控制

如上图所示。

原理图很容易理解,如果网络 MOTOR_PWM 为高电平,则 L9014 导通,蜂鸣器响,如果网络 MOTOR_PWM 为低电平,则 L9014 截止,蜂鸣器则不响。

操作方式和 led 小灯类似。

2、蜂鸣器测试例程

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

将编译成的可执行文件 buzzertest,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,使用参数 1 和 0,蜂鸣器会响。第二个参数实际上并不起作用(只有一个IO口,不需要判断)。运行程序如下。

Linux之字符设备控制Linux之字符设备控制

四、字符类 ADC 模数转换

1、ADC 模数转换简介

Linux之字符设备控制Linux之字符设备控制

如上图所示。

XadcAIN0 网络可以读取到当前输入电压,滑动变阻器 R 移动的时候,1 和 2 之间的电阻R12 改变,滑动变阻器最大电阻为 R13,然后电压

Vadc=R12VDD1V8_EXT/R13

上面公式中 Vadc 可以通过 4412 读取出来,VDD1V8 和 R13 已知,那么就很容易求出 R12的电阻。如下图所示,在 4412datasheet 中 ADC 章节中有真实的电阻和电压曲线图。

Linux之字符设备控制Linux之字符设备控制

这里将数值做一个简单的换算,

1.8V 对应的是 10K 欧姆,对应的寄存器数值为 0xfff;

0V 对应的是 0 欧姆,对应的寄存器数值为 0x0。

这样做一个简单公式,将读取的数值 r 转化为电阻值 R。

R = r10000/0xfff,即 R = r*10000/4095。

这个小公式在后面的代码中将会使用到。

2、模数转换例程

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

int main(void){
	int fd;
	char *adc = "/dev/adc";
	char buffer[512];
	int len=0, r=0;
	
	memset(buffer,0,sizeof(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);
		}			
	}
}
           

将编译成的可执行文件ADC,拷贝到 U 盘,启动开发板,插入 U 盘,加载 U 盘,运行程序如下。可检测当前电阻值,中间的大段打印参数是多次打印寄存器的数值,在驱动实验中再去介绍。调整滑动电阻器之后,再次使用测试程序,如下图所示,输出数值会有变化。

Linux之字符设备控制Linux之字符设备控制

继续阅读