六、字符设备控制
在 linux 驅(qū)動中字符驅(qū)動是必須掌握的,本章主要介紹字符設備應用的程序,無論是學習了后面的知識自己寫的字符驅(qū)動,還是已有的字符驅(qū)動,都需要能夠?qū)懸恍┖唵蔚膽贸绦颉?/p>
一、字符類led燈
1、led原理圖如下:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
2、程序設計
在前面介紹過,如果要給文件進行寫操作,那么使用的是 write 函數(shù)。對于 led 小燈的操作,使用寫函數(shù),理論上也是可以的。但是對于 IO 口(這里的 IO 口指的是硬件上的 IO 口,不是指 IO 文件)的操作,Linux 專門設計了一個高效的函數(shù) ioctl。這個函數(shù)在頭文件#include<unistd.h>中。
- int ioctl( int fd, int request, int cmd);?
? ? ? ? --參數(shù) fd,函數(shù) open 返回的句柄。
? ? ? ? --參數(shù) request 和參數(shù)cmd,由內(nèi)核驅(qū)動決定具體操作,例如 request 可以代表那個 IO 口,cmd 代表對 IO 進行什么樣的操作,也可以反過來。具體的含義由驅(qū)動工程師在驅(qū)動中 switch決定。?
? ? ? ? --返回值:返回 0 成功;返回-1,出錯。
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>#define LED_NUM 2 // LED個數(shù)(io為0則是靠近蜂鳴器的小燈,為1則靠近獨立按鍵的小燈) #define LED_CMD 2 // 控制命令個數(shù)(cmd為0,則滅,為1,則亮;)int main(int argc,char *argv[]) {int fd,led_num,led_c;char *leds = "/dev/leds";//leds驅(qū)動路徑led_num = LED_NUM;led_c = LED_CMD;printf("argv1 is cmd;argv2 is io \n"); //對傳入的參數(shù)進行判斷,超出范圍直接退出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函數(shù)將參數(shù)傳入內(nèi)核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、運行效果:
? ?
?
?
二、字符類Buzzer蜂鳴器
1、Buzzer原理圖如下:?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ??
?
2、程序設計
? Buzzer程序?qū)嶋H上和上面LED燈是一樣的,具體如下:
#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_CMD 2 // 響(高) / 不響(低) int main(int argc,char *argv[]){char *buzzer_ctl = "/dev/buzzer_ctl";// 蜂鳴器驅(qū)動文件int fd,ret,buzzer_c;buzzer_c = BUZZER_CMD;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])); // argv[2]實際上沒用,是通過argv[1]來控制close(fd);return 0; }3、運行效果:
? ? ??
三、字符類ADC模數(shù)轉(zhuǎn)換?
1、ADC原理圖如下:?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
如上圖所示。XadcAIN0 網(wǎng)絡可以讀取到當前輸入電壓,滑動變阻器 R 移動的時候,1 和 2 之間的電阻R12 改變,滑動變阻器最大電阻為 R13,然后電壓 Vadc=R12*VDD1V8_EXT/R13上面公式中 Vadc 可以通過 4412 讀取出來,VDD1V8 和 R13 已知,那么就很容易求出 R12的電阻。如下圖所示,在 4412datasheet 中 ADC 章節(jié)中有真實的電阻和電壓曲線圖。?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
這里將數(shù)值做一個簡單的換算:
? ? ? ? ? ?1.8V 對應的是 10K 歐姆,對應的寄存器數(shù)值為 0xfff;
? ? ? ? ? ? ?0?V 對應的是? ? ?0 歐姆,對應的寄存器數(shù)值為 0x00。
這樣做一個簡單公式,將讀取的數(shù)值 r 轉(zhuǎn)化為電阻值 R。
? ? ? ? ? R = r*10000/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> //#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));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); // 通過read函數(shù)讀取驅(qū)動內(nèi)獲得的AD值if(len == 0)printf("return null\n");else{r = atoi(buffer);r = (int)(r*10000/4095); //Datas transition to Resprintf("res value is %d\n",r);} } }3、運行效果:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
最后,我將整理的實驗源碼打包上傳,有需要的可以下載:
?
總結(jié)
- 上一篇: 五、文件IO函数
- 下一篇: 八、TFTP服务器搭建及应用