前两天学完了文件IO的操作,今天开始学习字符设备控制。
1.入口main函数
main函数作为应用程序的入口,在头文件#include <stdio.h>
中,其函数原型为:
int main(int argc,char **argv)
main函数的返回值类型为int,用来判断程序是否执行成功,其参数有两组:
-
参数argc,表示参数的个数(argument count),这个参数是系统统计自动传入的,不需要外部传入。
-
参数**argv,是用来存储输入字符的数组(argument value),数组中各元素的定义为:
argv[0],表示程序的名称,同argc一样,不需要外部传入。 argv[1]~argv[n],外部传入的n个参数
当main函数不传参数时,其定义为:
int main(void)
其例程如下:
include <stdio.h>
#include <string.h>
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 lie has %d argument:\n",argc-1);
printf("%d,%d\n",i,j);
return 0;
}
程序编译运行后,当外部输入参数为9 5时,在终端打印的输出为:
the Program name is ./c_argvc
The command lie has 2 argument:
9,5
2.字符类设备之——led灯
打开字符类设备,获得句柄的方法和一般的文件一样是通过open函数实现的,led灯的设备节点在/dev目录下,如下图所示:
image.png 因为点亮led灯涉及到硬件原理,这里把对应的原理图贴出来:
image.png 如图所示,在外部的硬件连接中,使用了三极管9014作为开关,当输出高点平时,三极管导通,对应led灯被点亮,否则三极管截止,对应led灯被关闭。
在前面介绍过,如果要给文件进行写操作,那么使用的是write 函数。对于led 小灯的操作,使用写函数,理论上也是可以的。但是对于IO 口(这里的IO 口指的是硬件上的IO 口,不是指IO 文件)的操作,Linux 专门设计了一个高效的函数ioctl。
ioctl函数的原型为:
int ioctl(int fd,int request,int cmd);
该函数有三个参数,其返回值若为0表示成功,为-1则表示出错:
- 第一个参数fd,为对应文件的句柄值。
- 第二个参数request,可以代表哪个IO口,或者代表对IO进行怎样的操作,即在选择和控制中二选一,具体含义由驱动中switch决定。
- 第三个参数cmd,与第二个参数request相同,对应剩余的一种操作。
其例程如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define LED_NUM 2
#define LED_CTL 2
//request为0,则选中靠近蜂鸣器的led,为1则为靠近按键的led
//cmd为0,则对应led灭,为1则亮
int main(int argc,char *argv[])
{
int fd,ctl,led_num,led_ctl,in_request,in_cmd;
char *leds = "/dev/leds";
led_num = LED_NUM;
led_ctl = LED_CTL;
in_request = atoi(argv[2]);
in_cmd = atoi(argv[1]);
printf("argv1 is cmd;and argv2 is request\n");
if(in_request >= led_num)
{
printf("argv1 can not more than 1!\n");
exit(1);
}
if(in_cmd >= led_ctl)
{
printf("argv2 can not more than 1!\n");
exit(1);
}
if((fd = open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0)
{
printf("open %s failed\n",leds);
}
else
{
ctl = ioctl(fd,in_request,in_cmd);
if(ctl == -1)
{
printf("contrl %s failed!\n",leds);
}
else
{
printf("contrl %s success!\n",leds);
}
}
close(fd);
return(1);
}
将例程编译下载到开发板上运行,可以观察到开发板上对应led灯对不同的输入的反应,且在终端上会打印出相应信息。
3.字符类设备之——蜂鸣器
蜂鸣器的控制与例程与led灯相似,唯一的不同在于控制蜂鸣器的ioctl函数只需要两个参数的输入,因为蜂鸣器只有一个且状态只有响和不响两种,因此除了fd参数外,外部只需要传入一个参数便能完成控制。由于其与led控制相似度大因而学习意义不大,具体步骤这里便不再赘述。
4.字符类设备之——ADC模数转换
与led相同,adc的设备节点也是在/dev/目录下,如图:
image.png ADC的硬件原理图则如下图:
image.png 由图上可以看出,输入电压值为滑动变阻器从VDD(1.8V)上分压得到。
ADC的测试例程为:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <termios.h>
int main()
{
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 %s err!\n",adc);
}
else
{
printf("open %s success!\n",adc);
len = read(fd,buffer,10);
if(len == 0)
{
printf("return null\n");
}
else
{
r = atoi(buffer);
r = (int)(r*10000/4095);
printf("the register value is %d ohm!\n",r);
}
}
}
例程中用到了menset函数,在这里作用是讲开辟出的数组空间清零。
编译通过下载到开发板上运行,在终端上将打印出测到的电阻值,转动滑动变阻器,测到的电阻值也会相应改变:
MHO43(8NPT@9IT){{{4O$IG.png5.结束语
又是两个晚上,完成了字符类设备控制的学习,这一章的学习感觉十分粗浅,并没有什么深入的内容,但是对于刚接触的同学来说能够看到对应的现象应该是一个不小的鼓励吧希望继续加油,共勉
网友评论